/* * This file is part of Caliph & Emir. * * Caliph & Emir is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Caliph & Emir is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Caliph & Emir; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright statement: * -------------------- * (c) 2002-2005 by Mathias Lux (mathias@juggle.at) * http://www.juggle.at, http://caliph-emir.sourceforge.net */ package at.lux.fotoretrieval; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.event.*; import java.io.*; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; import java.util.Set; import java.util.Vector; import javax.imageio.ImageIO; import javax.swing.*; import org.apache.lucene.index.IndexReader; import org.jdom.Element; import at.lux.components.StatusBar; import at.lux.fotoannotation.AnnotationToolkit; import at.lux.fotoannotation.AnnotationFrame; import at.lux.fotoannotation.IconCache; import at.lux.fotoannotation.dialogs.FullSizeImagePanel; import at.lux.fotoretrieval.dialogs.AboutDialog; import at.lux.fotoretrieval.dialogs.HelpDialog; import at.lux.fotoretrieval.dialogs.IndexLocationDialog; import at.lux.fotoretrieval.dialogs.IndexingWizardDialog; import at.lux.fotoretrieval.lucene.IndexerThread; import at.lux.fotoretrieval.panels.*; import at.lux.fotoretrieval.retrievalengines.LucenePathIndexRetrievalEngine; import at.lux.fotoretrieval.retrievalengines.LuceneRetrievalEngine; import at.lux.imageanalysis.JDomVisualDescriptor; import at.lux.imageanalysis.VisualDescriptor; import at.lux.splash.SplashScreen; import com.jgoodies.looks.plastic.PlasticLookAndFeel; /** * Main class for retrieval ... */ public class RetrievalFrame extends JFrame implements ActionListener, RetrievalOperations, StatusBar { public static final boolean DEBUG = false; public static final String CONFIGURATION_FILE = "emir.properties"; public static String BASE_DIRECTORY = "."; private JPanel statusPanel; private JProgressBar garbageBar; private JLabel status; private JTabbedPane tabs; private ContentBasedImageRetrievalPanel cbPanel; private GarbageTracker gTracker; private TextInputSearchPanel searchPanel; private XPathSearchPanel xpathInputPanel; private LuceneSearchPanel luceneInputPanel; private Properties props; private SemanticSearchPanel semanticSearchPanel; private GraphSearchPanel graphSearchPanel; private final String windowSizeX = "window.size.x"; private final String windowSizeY = "window.size.y"; private final String windowLocationX = "window.location.x"; private final String windowLocationY = "window.location.y"; private final String dataRepositoryBaseDirectory = "data.base.directory"; private JDialog configDialog = null; public RetrievalFrame() { super(); try { this.setIconImage(ImageIO.read(RetrievalFrame.class.getResource("data/find.gif"))); } catch (Exception e) { debug("Couldn't set Icon: " + e.toString() + " " + e.getMessage()); } init(); } private void init() { setTitle(RetrievalToolkit.PROGRAM_NAME + ' ' + AnnotationToolkit.PROGRAM_VERSION); // ---------------------------- // Setting Emir props // ---------------------------- props = new Properties(); File conf = new File("./" + CONFIGURATION_FILE); debug("./" + CONFIGURATION_FILE); if (conf.exists()) { try { props.load(new FileInputStream(conf)); } catch (IOException e) { System.err.println("Error loading config file: " + e.toString()); } BASE_DIRECTORY = props.getProperty(dataRepositoryBaseDirectory, "."); } else { props.setProperty(dataRepositoryBaseDirectory, BASE_DIRECTORY); } // creating a base configuration from saved properties EmirConfiguration.getInstance(props); // ---------------------------- // Setting Frame props // ---------------------------- int xwidth = Integer.parseInt(props.getProperty(windowSizeX, "640")); int ywidth = Integer.parseInt(props.getProperty(windowSizeY, "480")); setSize(xwidth, ywidth); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); int xloc = Integer.parseInt(props.getProperty(windowLocationX, "" + ((d.width - this.getWidth()) / 2))); int yloc = Integer.parseInt(props.getProperty(windowLocationY, "" + ((d.height - this.getHeight()) / 2))); setLocation(xloc, yloc); JMenuBar retrievalMenuBar = RetrievalToolkit.createRetrievalMenuBar(this); retrievalMenuBar.add(createResultsPanelMenu(), retrievalMenuBar.getComponentCount() - 1); this.setJMenuBar(retrievalMenuBar); // ---------------------------- // Setting Frame Listeners ... // ---------------------------- this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exitApplication(); } }); // ---------------------------- // Creating Components // ---------------------------- cbPanel = new ContentBasedImageRetrievalPanel(this); searchPanel = new TextInputSearchPanel(this); xpathInputPanel = new XPathSearchPanel(this); luceneInputPanel = new LuceneSearchPanel(this); semanticSearchPanel = new SemanticSearchPanel(this); graphSearchPanel = new GraphSearchPanel(this); statusPanel = new JPanel(new BorderLayout()); status = new JLabel(RetrievalToolkit.PROGRAM_NAME + " " + AnnotationToolkit.PROGRAM_VERSION); status.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); garbageBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 16); garbageBar.setStringPainted(true); garbageBar.setBorder(BorderFactory.createEmptyBorder()); garbageBar.setToolTipText("This bar shows how much memory is allocated by the VM and how much of it isalready in use."); // garbageBar.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); gTracker = new GarbageTracker(garbageBar); statusPanel.add(status, BorderLayout.CENTER); statusPanel.add(garbageBar, BorderLayout.EAST); tabs = new JTabbedPane(JTabbedPane.NORTH); tabs.add("Index", luceneInputPanel); tabs.add("Graph", graphSearchPanel); tabs.add("Image", cbPanel); // tabs.add("Keywords", searchPanel); // tabs.add("XPath", xpathInputPanel); // tabs.add("Semantics", semanticSearchPanel); this.getContentPane().add(tabs, BorderLayout.CENTER); this.getContentPane().add(statusPanel, BorderLayout.SOUTH); gTracker.start(); } private JMenu createResultsPanelMenu() { JMenu resultsMenu = new JMenu("Results"); resultsMenu.setMnemonic(KeyEvent.VK_R); JMenuItem saveResults = new JMenuItem("Export result list ...", IconCache.getInstance().getSaveAsIcon()); saveResults.setAccelerator(KeyStroke.getKeyStroke("ctrl S")); saveResults.addActionListener(this); saveResults.setActionCommand("exportResultList"); JMenuItem clusterResults = new JMenuItem("Cluster results", IconCache.getInstance().getClusterIcon()); clusterResults.setAccelerator(KeyStroke.getKeyStroke("F2")); clusterResults.addActionListener(this); clusterResults.setActionCommand("clusterResults"); JMenuItem mdsResults = new JMenuItem("Visualize results", IconCache.getInstance().getMdsIcon()); mdsResults.setAccelerator(KeyStroke.getKeyStroke("F3")); mdsResults.addActionListener(this); mdsResults.setActionCommand("mdsResults"); resultsMenu.add(saveResults); resultsMenu.addSeparator(); resultsMenu.add(clusterResults); resultsMenu.add(mdsResults); return resultsMenu; } public static void main(String[] args) { JWindow w = null; try { w = new JWindow(); BufferedImage img = ImageIO.read(AnnotationFrame.class.getResourceAsStream("data/SplashEmir.png")); FullSizeImagePanel panel = new FullSizeImagePanel(img); w.getContentPane().add(panel, BorderLayout.CENTER); w.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); w.setLocation((screenSize.width - w.getWidth()) / 2, (screenSize.height - w.getHeight()) / 2); w.setVisible(true); } catch (IOException e) { e.printStackTrace(); } try { // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(new PlasticLookAndFeel()); } catch (Exception e) { System.err.println("Could not set Look & Feel: " + e.toString()); } boolean showSplash = true; // get config file from splashscreen class File splashProps = new File(SplashScreen.LICENSE_ACCEPTED_FILENAME); // look if exists if (splashProps.exists()) { // load props try { Properties licenseAcceptedProps = new Properties(); licenseAcceptedProps.load(new FileInputStream(splashProps)); // if property is set we do not have to show the splashscreen: String tmp = licenseAcceptedProps.getProperty("license.accepted"); if (tmp != null && tmp.equals("true")) { showSplash = false; } } catch (IOException e) { System.err.println("Warn: Could not read license properties file."); } } try { RetrievalFrame rf = new RetrievalFrame(); SplashScreen splash = new SplashScreen(rf); if (showSplash) { // now show the splash screen if this has not been done before. splash.setVisible(true); } w.setVisible(false); rf.setVisible(true); } catch (Exception e) { JOptionPane.showConfirmDialog(null, "Error: " + e.toString(), "Error starting Emir!", JOptionPane.OK_OPTION, JOptionPane.ERROR_MESSAGE); e.printStackTrace(); System.exit(0); } } /** * Overides method defined in Interface ActionListener */ public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("exit")) { exitApplication(); } else if (e.getActionCommand().equals("index")) { createIndex(); } else if (e.getActionCommand().equals("closeTab")) { if (tabs.getSelectedIndex() > 2) { tabs.remove(tabs.getSelectedIndex()); } } else if (e.getActionCommand().equals("about")) { AboutDialog adialog = new AboutDialog(this); adialog.pack(); Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); adialog.setLocation((ss.width - adialog.getWidth()) / 2, (ss.height - adialog.getHeight()) / 2); adialog.setVisible(true); } else if (e.getActionCommand().equals("k-tab")) { tabs.add("Keywords", searchPanel); tabs.setSelectedComponent(searchPanel); } else if (e.getActionCommand().equals("s-tab")) { tabs.add("Semantics", semanticSearchPanel); tabs.setSelectedComponent(semanticSearchPanel); } else if (e.getActionCommand().equals("x-tab")) { tabs.add("XPath", xpathInputPanel); tabs.setSelectedComponent(xpathInputPanel); } else if (e.getActionCommand().equals("wizardIndex")) { IndexingWizardDialog wizardDialog = new IndexingWizardDialog(this); } else if (e.getActionCommand().equals("visitHomepage")) { openUrlInBrowser(props.getProperty("help.homepage")); } else if (e.getActionCommand().equals("showHelpOnline")) { openUrlInBrowser(props.getProperty("help.online")); } else if (e.getActionCommand().equals("showConfig")) { if (configDialog == null) { configDialog = new JDialog((Frame) null, "Emir :: Configuration"); configDialog.setSize(640, 480); Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); configDialog.setLocation((ss.width - configDialog.getWidth())/2, (ss.height-configDialog.getHeight())/2); ConfigurationDialogPanel config = new ConfigurationDialogPanel(configDialog); configDialog.getContentPane().add(config, BorderLayout.CENTER); } configDialog.setVisible(true); } else if (e.getActionCommand().equals("createIndex")) { createIndex(); } else if (e.getActionCommand().equals("indexPath")) { IndexLocationDialog dg = new IndexLocationDialog(this); dg.pack(); Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); dg.setLocation(((int) d.getWidth() - dg.getWidth()) >> 1, ((int) d.getHeight() - dg.getHeight()) >> 1); dg.setVisible(true); } else if (e.getActionCommand().equals("viCl")) { // visualize current repository using ColorLayout: JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); VisualDescriptorVisualizationThread vt = new VisualDescriptorVisualizationThread(BASE_DIRECTORY, this, pbar, JDomVisualDescriptor.Type.ColorLayout); vt.start(); } else if (e.getActionCommand().equals("viSc")) { // visualize current repository using ScalableColor: JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); VisualDescriptorVisualizationThread vt = new VisualDescriptorVisualizationThread(BASE_DIRECTORY, this, pbar, JDomVisualDescriptor.Type.ScalableColor); vt.start(); } else if (e.getActionCommand().equals("viEh")) { // visualize current repository using EdgeHistogram: JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); VisualDescriptorVisualizationThread vt = new VisualDescriptorVisualizationThread(BASE_DIRECTORY, this, pbar, JDomVisualDescriptor.Type.EdgeHistogram); vt.start(); } else if (e.getActionCommand().equals("viSg")) { // visualize current repository using semantic graphs: if (true) { JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); GraphDistanceVisualizationThread vt = new GraphDistanceVisualizationThread(BASE_DIRECTORY, this, pbar); vt.start(); } else { JOptionPane.showMessageDialog(this, "Visualization not available.\nNot fully implemented with new index structure!"); } } else if (e.getActionCommand().equals("showIndexPath")) { JOptionPane.showMessageDialog(this, "Current data repository:\n " + BASE_DIRECTORY); setStatus("Current data repository: " + BASE_DIRECTORY); } else if (e.getActionCommand().equals("exportResultList")) { try { Component component = tabs.getSelectedComponent(); if (component instanceof ResultsPanel) { File tempFile = File.createTempFile("emir_results_", ".html"); String html = ((ResultsPanel) component).getResultHtml(); BufferedWriter bw = new BufferedWriter(new FileWriter(tempFile)); bw.write(html); bw.close(); openUrlInBrowser(tempFile.getCanonicalPath()); } } catch (IOException e1) { JOptionPane.showMessageDialog(this, "Error saving description: " + e.toString()); } } else if (e.getActionCommand().equals("clusterResults")) { // TODO: implement this ... JOptionPane.showMessageDialog(this, "Not implemented yet!"); } else if (e.getActionCommand().equals("mdsResults")) { // JOptionPane.showMessageDialog(this, "Not implemented yet!"); Component component = tabs.getSelectedComponent(); if (component instanceof ResultsPanel) { String[] files = ((ResultsPanel) component).getResultFiles(); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); int metric = Integer.parseInt(EmirConfiguration.getInstance().getProperty("Metric.FDP")); if (metric == 0 || metric == 1 || metric == 3) { VisualDescriptor.Type metricType = JDomVisualDescriptor.Type.ScalableColor; if (metric == 1) metricType = VisualDescriptor.Type.ColorLayout; if (metric == 2) metricType = VisualDescriptor.Type.DominantColor; if (metric == 3) metricType = VisualDescriptor.Type.EdgeHistogram; VisualDescriptorVisualizationThread vt = new VisualDescriptorVisualizationThread(files, this, pbar, metricType); vt.start(); } else if (metric == 4) { JOptionPane.showMessageDialog(this, "Not implemented yet! Only ScalableColor, ColorLayout and EdgeHistogram are implemented for visualizing results."); // GraphDistanceVisualizationThread vt = new GraphDistanceVisualizationThread(".", this, pbar); } else if (metric == 6) { GraphDistanceVisualizationThread vt = new GraphDistanceVisualizationThread(files, this, pbar); vt.start(); } else { JOptionPane.showMessageDialog(this, "Not implemented yet! Only ScalableColor, ColorLayout and EdgeHistogram are implemented for visualizing results."); } } } else if (e.getActionCommand().equals("help")) { HelpDialog adialog = new HelpDialog(this); // adialog.pack(); Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); adialog.setLocation((ss.width - adialog.getWidth()) / 2, (ss.height - adialog.getHeight()) / 2); adialog.setVisible(true); } else { JOptionPane.showMessageDialog(this, "Not implemented yet!"); } } private void openUrlInBrowser(String url) { String osName = System.getProperty("os.name"); // take linux settings String browserCmd = props.getProperty("browser.linux"); // or windows in case of windows :) if (osName.toLowerCase().indexOf("windows") > -1) { browserCmd = props.getProperty("browser.windows"); } browserCmd = browserCmd.replace("{url}", url); try { Runtime.getRuntime().exec(browserCmd); } catch (IOException e) { JOptionPane.showMessageDialog(this, "Could not start browser. Please surf to " + url + " to visit the online help."); } } public void createIndex() { int answer = JOptionPane.showConfirmDialog(this, "Do you really want to create an index?"); if (answer == JOptionPane.OK_OPTION) { Thread indexer = new Thread(new IndexerThread(this, BASE_DIRECTORY)); indexer.start(); } } /** * is called before program is exited. */ private void exitApplication() { debug("Exiting application"); props.setProperty(dataRepositoryBaseDirectory, BASE_DIRECTORY); props.setProperty(windowSizeX, this.getWidth() + ""); props.setProperty(windowSizeY, this.getHeight() + ""); props.setProperty(windowLocationX, this.getLocation().x + ""); props.setProperty("yloc", this.getLocation().y + ""); props = EmirConfiguration.getInstance().saveProperties(props); try { props.store(new FileOutputStream("./" + CONFIGURATION_FILE, false), "Emir configuration file (automatically generated)"); debug("Saved configuration to " + "./" + CONFIGURATION_FILE); } catch (IOException e) { debug("Couldn't store " + "./" + CONFIGURATION_FILE); } // shutting down the database: try { DriverManager.getConnection("jdbc:derby:imageDB;shutdown=true"); } catch (SQLException e) { System.out.println("DB shutdown successful!"); } System.exit(0); } private void debug(String message) { if (RetrievalFrame.DEBUG) { System.out.println("[at.lux.fotoretrieval.RetrievalFrame] " + message); } } public void searchForImage(Element ColorLayoutDescriptor, String directory, boolean descendIntoSubdirs) { debug("Starting search for similar image"); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = null; JCheckBoxMenuItem item = (JCheckBoxMenuItem) getJMenuBar().getMenu(3).getItem(2); if (!item.getState()) { t = new SimilarImageSearchThread(ColorLayoutDescriptor, directory, descendIntoSubdirs, this, pbar); } else { t = new DatabaseSimilarImageSearchThread(ColorLayoutDescriptor, directory, descendIntoSubdirs, this, pbar); } t.start(); } public void searchForImage(Set<Element> ColorLayoutDescriptor, String directory, boolean descendIntoSubdirs) { debug("Starting search for similar image"); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = null; boolean useDerby = EmirConfiguration.getInstance().getBoolean("Retrieval.Cbir.useDerby"); if (!useDerby) { t = new SimilarImageSearchThread(ColorLayoutDescriptor, directory, descendIntoSubdirs, this, pbar); } else { t = new DatabaseSimilarImageSearchThread(ColorLayoutDescriptor, directory, descendIntoSubdirs, this, pbar); } t.start(); } public void searchForImage(String xPath, String directory, boolean descendIntoSubdirs) { debug("Starting search for keywords ... "); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = new SearchThroughXPathThread(xPath, directory, descendIntoSubdirs, this, pbar); t.start(); } public void searchForImageInIndex(String xPath, String directory, boolean descendIntoSubdirs) { if (IndexReader.indexExists(LuceneRetrievalEngine.parseFulltextIndexDirectory(BASE_DIRECTORY)) && IndexReader.indexExists(LuceneRetrievalEngine.parseSemanticIndexDirectory(BASE_DIRECTORY))) { debug("Starting search for keywords ... "); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = new SearchThroughLuceneThread(xPath, directory, descendIntoSubdirs, this, pbar); t.start(); } else { setStatus("Using index from " + BASE_DIRECTORY); JOptionPane.showMessageDialog(this, "Given directory (" + BASE_DIRECTORY + ") contains no index, please \ncreate one first (see menu)."); } } public void searchForImage(String xPath, Vector objects, String directory, boolean descendIntoSubdirs) { debug("Starting search for semantic description ... "); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = new SearchSemanticDescriptionThread(xPath, objects, directory, descendIntoSubdirs, this, pbar); t.start(); } public void setStatus(String message) { status.setText(message); } public void addResult(ResultsPanel rp) { tabs.add("Results", rp); tabs.setSelectedIndex(tabs.getTabCount() - 1); } public void addVisualization(JPanel p) { tabs.add("Vis", p); tabs.setSelectedIndex(tabs.getTabCount() - 1); } public QualityConstraintPanel getQualityConstraints() { return searchPanel.getQualityConstraints(); } public void searchForSemanticsInIndex(String xPath, String directory, boolean descendIntoSubdirs) { if (IndexReader.indexExists(LuceneRetrievalEngine.parseFulltextIndexDirectory(BASE_DIRECTORY)) && IndexReader.indexExists(LuceneRetrievalEngine.parseSemanticIndexDirectory(BASE_DIRECTORY))) { debug("Starting search for semantic annotations ... "); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = new SearchSemanticInIndexThread(xPath, directory, descendIntoSubdirs, this, pbar); t.start(); } else { setStatus("Using index from " + BASE_DIRECTORY); JOptionPane.showMessageDialog(this, "Given directory (" + BASE_DIRECTORY + ") contains no index, please \ncreate one first (see menu)."); } } public void searchForSemanticsInPathIndex(String xPath, String directory, boolean descendIntoSubdirs) { if (IndexReader.indexExists(LucenePathIndexRetrievalEngine.parsePathIndexDirectory(BASE_DIRECTORY)) && IndexReader.indexExists(LuceneRetrievalEngine.parseSemanticIndexDirectory(BASE_DIRECTORY))) { debug("Starting search for semantic annotations ... "); JProgressBar pbar = new JProgressBar(JProgressBar.HORIZONTAL); pbar.setStringPainted(true); Thread t = new SearchSemanticInPathIndexThread(xPath, directory, descendIntoSubdirs, this, pbar); t.start(); } else { setStatus("Using index from " + BASE_DIRECTORY); JOptionPane.showMessageDialog(this, "Given directory (" + BASE_DIRECTORY + ") contains no index, please \ncreate one first (see menu)."); } } }