From: lingutln Date: Mon, 7 Nov 2011 19:52:57 +0000 (+0000) Subject: Uploaded initial version of image annotation project X-Git-Url: http://gitweb.planteome.org/?a=commitdiff_plain;h=c899faab79c601534d5ee5e971954c67f63c7f34;p=old-jaiswallab-svn%2F.git Uploaded initial version of image annotation project svn path=/; revision=212 --- diff --git a/Personnel/lingutln/.gitignore b/Personnel/lingutln/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/image_annotation/.gitignore b/image_annotation/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/image_annotation/bin/BPTPlugin.plugin/bptplugin.jar b/image_annotation/bin/BPTPlugin.plugin/bptplugin.jar new file mode 100644 index 0000000..d4025cf Binary files /dev/null and b/image_annotation/bin/BPTPlugin.plugin/bptplugin.jar differ diff --git a/image_annotation/bin/BPTPlugin.plugin/jbpt.dll b/image_annotation/bin/BPTPlugin.plugin/jbpt.dll new file mode 100644 index 0000000..d12f777 Binary files /dev/null and b/image_annotation/bin/BPTPlugin.plugin/jbpt.dll differ diff --git a/image_annotation/bin/BPTPlugin.plugin/libjbpt.jnilib b/image_annotation/bin/BPTPlugin.plugin/libjbpt.jnilib new file mode 100644 index 0000000..77039d7 Binary files /dev/null and b/image_annotation/bin/BPTPlugin.plugin/libjbpt.jnilib differ diff --git a/image_annotation/bin/BPTPlugin.plugin/plugin.xml b/image_annotation/bin/BPTPlugin.plugin/plugin.xml new file mode 100644 index 0000000..4ae529d --- /dev/null +++ b/image_annotation/bin/BPTPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/bin/IGCPlugin.plugin/igcplugin.jar b/image_annotation/bin/IGCPlugin.plugin/igcplugin.jar new file mode 100644 index 0000000..c85393b Binary files /dev/null and b/image_annotation/bin/IGCPlugin.plugin/igcplugin.jar differ diff --git a/image_annotation/bin/IGCPlugin.plugin/jigc.dll b/image_annotation/bin/IGCPlugin.plugin/jigc.dll new file mode 100644 index 0000000..b4ee98c Binary files /dev/null and b/image_annotation/bin/IGCPlugin.plugin/jigc.dll differ diff --git a/image_annotation/bin/IGCPlugin.plugin/libjigc.jnilib b/image_annotation/bin/IGCPlugin.plugin/libjigc.jnilib new file mode 100644 index 0000000..6ff9817 Binary files /dev/null and b/image_annotation/bin/IGCPlugin.plugin/libjigc.jnilib differ diff --git a/image_annotation/bin/IGCPlugin.plugin/plugin.xml b/image_annotation/bin/IGCPlugin.plugin/plugin.xml new file mode 100644 index 0000000..2b7ae2e --- /dev/null +++ b/image_annotation/bin/IGCPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/bin/SIOXPlugin.plugin/plugin.xml b/image_annotation/bin/SIOXPlugin.plugin/plugin.xml new file mode 100644 index 0000000..623ad7b --- /dev/null +++ b/image_annotation/bin/SIOXPlugin.plugin/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/image_annotation/bin/SIOXPlugin.plugin/sioxapi.jar b/image_annotation/bin/SIOXPlugin.plugin/sioxapi.jar new file mode 100644 index 0000000..4c121b2 Binary files /dev/null and b/image_annotation/bin/SIOXPlugin.plugin/sioxapi.jar differ diff --git a/image_annotation/bin/SIOXPlugin.plugin/sioxplugin.jar b/image_annotation/bin/SIOXPlugin.plugin/sioxplugin.jar new file mode 100644 index 0000000..31f959a Binary files /dev/null and b/image_annotation/bin/SIOXPlugin.plugin/sioxplugin.jar differ diff --git a/image_annotation/bin/SRGPlugin.plugin/jsrg.dll b/image_annotation/bin/SRGPlugin.plugin/jsrg.dll new file mode 100644 index 0000000..c381fae Binary files /dev/null and b/image_annotation/bin/SRGPlugin.plugin/jsrg.dll differ diff --git a/image_annotation/bin/SRGPlugin.plugin/libjsrg.jnilib b/image_annotation/bin/SRGPlugin.plugin/libjsrg.jnilib new file mode 100644 index 0000000..46427e8 Binary files /dev/null and b/image_annotation/bin/SRGPlugin.plugin/libjsrg.jnilib differ diff --git a/image_annotation/bin/SRGPlugin.plugin/plugin.xml b/image_annotation/bin/SRGPlugin.plugin/plugin.xml new file mode 100644 index 0000000..4061f98 --- /dev/null +++ b/image_annotation/bin/SRGPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/bin/SRGPlugin.plugin/srgplugin.jar b/image_annotation/bin/SRGPlugin.plugin/srgplugin.jar new file mode 100644 index 0000000..c888c62 Binary files /dev/null and b/image_annotation/bin/SRGPlugin.plugin/srgplugin.jar differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$1.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$1.class new file mode 100644 index 0000000..3e1e74f Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$2.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$2.class new file mode 100644 index 0000000..1356599 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$Keys.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$Keys.class new file mode 100644 index 0000000..13fa3dc Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$Keys.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$SupportedTypes.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$SupportedTypes.class new file mode 100644 index 0000000..337ba2e Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs$SupportedTypes.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefs.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs.class new file mode 100644 index 0000000..3eb1f25 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefs.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppPrefsManager.class b/image_annotation/bin/ie/dcu/apps/ist/AppPrefsManager.class new file mode 100644 index 0000000..651127c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppPrefsManager.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppRecentFiles.class b/image_annotation/bin/ie/dcu/apps/ist/AppRecentFiles.class new file mode 100644 index 0000000..a8f73c5 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppRecentFiles.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$1.class b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$1.class new file mode 100644 index 0000000..9c6f352 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$2.class b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$2.class new file mode 100644 index 0000000..7960862 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$EmptyAction.class b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$EmptyAction.class new file mode 100644 index 0000000..75a1d08 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu$EmptyAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu.class b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu.class new file mode 100644 index 0000000..c993dae Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppRecentMenu.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppStatus.class b/image_annotation/bin/ie/dcu/apps/ist/AppStatus.class new file mode 100644 index 0000000..c877538 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppStatus.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppWindow$1.class b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$1.class new file mode 100644 index 0000000..4bdc205 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppWindow$2.class b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$2.class new file mode 100644 index 0000000..21e30e0 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppWindow$3.class b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$3.class new file mode 100644 index 0000000..0b7745f Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppWindow$ImageObserver.class b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$ImageObserver.class new file mode 100644 index 0000000..dfc6a8b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppWindow$ImageObserver.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/AppWindow.class b/image_annotation/bin/ie/dcu/apps/ist/AppWindow.class new file mode 100644 index 0000000..23c945c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/AppWindow.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/Application.class b/image_annotation/bin/ie/dcu/apps/ist/Application.class new file mode 100644 index 0000000..c1be587 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/Application.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/EvaluatorRegistry.class b/image_annotation/bin/ie/dcu/apps/ist/EvaluatorRegistry.class new file mode 100644 index 0000000..55365d3 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/EvaluatorRegistry.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/Main.class b/image_annotation/bin/ie/dcu/apps/ist/Main.class new file mode 100644 index 0000000..623c934 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/Main.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/PainterRegistry.class b/image_annotation/bin/ie/dcu/apps/ist/PainterRegistry.class new file mode 100644 index 0000000..f257b72 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/PainterRegistry.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/SegmenterRegistry.class b/image_annotation/bin/ie/dcu/apps/ist/SegmenterRegistry.class new file mode 100644 index 0000000..a4d4fde Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/SegmenterRegistry.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction$AboutBox.class b/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction$AboutBox.class new file mode 100644 index 0000000..77e8e60 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction$AboutBox.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction.class new file mode 100644 index 0000000..c31de41 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/AboutAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ActionManager.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ActionManager.class new file mode 100644 index 0000000..032267e Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ActionManager.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/AppAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/AppAction.class new file mode 100644 index 0000000..15ee7a5 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/AppAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.class new file mode 100644 index 0000000..3050adf Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ConfiguredAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ConfiguredAction.class new file mode 100644 index 0000000..80e3be7 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ConfiguredAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction$ImageSelection.class b/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction$ImageSelection.class new file mode 100644 index 0000000..7f13bb1 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction$ImageSelection.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction.class new file mode 100644 index 0000000..550bd90 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/CopyAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ExitAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ExitAction.class new file mode 100644 index 0000000..d87ce2b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ExitAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ExportImageMapAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportImageMapAction.class new file mode 100644 index 0000000..8f0224d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportImageMapAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.class new file mode 100644 index 0000000..c4d82e4 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/ExportViewAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportViewAction.class new file mode 100644 index 0000000..a74bd89 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/ExportViewAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox$1.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox$1.class new file mode 100644 index 0000000..43e3dce Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox.class new file mode 100644 index 0000000..d001d7d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction$HelpBox.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction.class new file mode 100644 index 0000000..dd7b846 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HelpAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HoverAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverAction.class new file mode 100644 index 0000000..de62c98 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$1.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$1.class new file mode 100644 index 0000000..e04e53b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$HoverListener.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$HoverListener.class new file mode 100644 index 0000000..5e058f1 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager$HoverListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager.class b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager.class new file mode 100644 index 0000000..c27e1b4 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/HoverMenuManager.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/IHoverAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/IHoverAction.class new file mode 100644 index 0000000..f57a6ea Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/IHoverAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/NextAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/NextAction.class new file mode 100644 index 0000000..1c4d433 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/NextAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/OpenAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenAction.class new file mode 100644 index 0000000..ba68616 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/OpenExperimentAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenExperimentAction.class new file mode 100644 index 0000000..4ec3ca9 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenExperimentAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/OpenRecentAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenRecentAction.class new file mode 100644 index 0000000..115347d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/OpenRecentAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/PreferencesAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/PreferencesAction.class new file mode 100644 index 0000000..9937282 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/PreferencesAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/PreviousAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/PreviousAction.class new file mode 100644 index 0000000..492d688 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/PreviousAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/PrintAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/PrintAction.class new file mode 100644 index 0000000..818b060 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/PrintAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/RedoAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/RedoAction.class new file mode 100644 index 0000000..e6c3d2c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/RedoAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAction.class new file mode 100644 index 0000000..127a834 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAsAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAsAction.class new file mode 100644 index 0000000..f209d77 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/SaveAsAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/SelectSegmenterAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/SelectSegmenterAction.class new file mode 100644 index 0000000..a7c8283 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/SelectSegmenterAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/SimpleFileFilter.class b/image_annotation/bin/ie/dcu/apps/ist/actions/SimpleFileFilter.class new file mode 100644 index 0000000..7c65f92 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/SimpleFileFilter.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/actions/UndoAction.class b/image_annotation/bin/ie/dcu/apps/ist/actions/UndoAction.class new file mode 100644 index 0000000..1b88f46 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/actions/UndoAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/controllers/AnnotationTool.class b/image_annotation/bin/ie/dcu/apps/ist/controllers/AnnotationTool.class new file mode 100644 index 0000000..ffe0a3b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/controllers/AnnotationTool.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/controllers/MouseMotionAdapter.class b/image_annotation/bin/ie/dcu/apps/ist/controllers/MouseMotionAdapter.class new file mode 100644 index 0000000..b4e6a7a Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/controllers/MouseMotionAdapter.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$1.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$1.class new file mode 100644 index 0000000..5998e37 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$2.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$2.class new file mode 100644 index 0000000..95be366 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$3.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$3.class new file mode 100644 index 0000000..416ae9b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$4.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$4.class new file mode 100644 index 0000000..672bde2 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$4.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$Result.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$Result.class new file mode 100644 index 0000000..69e7e9b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog$Result.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog.class new file mode 100644 index 0000000..f91e5fb Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/ExportDialog.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog$1.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog$1.class new file mode 100644 index 0000000..383ee6c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog.class b/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog.class new file mode 100644 index 0000000..1a6d789 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/dialogs/PrefsDialog.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangeListener.class b/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangeListener.class new file mode 100644 index 0000000..8452af6 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangeListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangedEvent.class b/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangedEvent.class new file mode 100644 index 0000000..627034f Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/ContextChangedEvent.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/StateEvent.class b/image_annotation/bin/ie/dcu/apps/ist/event/StateEvent.class new file mode 100644 index 0000000..47ed701 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/StateEvent.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/StateListener.class b/image_annotation/bin/ie/dcu/apps/ist/event/StateListener.class new file mode 100644 index 0000000..c4aaae3 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/StateListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/TickerEvent.class b/image_annotation/bin/ie/dcu/apps/ist/event/TickerEvent.class new file mode 100644 index 0000000..0b9daff Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/TickerEvent.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/TickerListener.class b/image_annotation/bin/ie/dcu/apps/ist/event/TickerListener.class new file mode 100644 index 0000000..45511b3 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/TickerListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutEvent.class b/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutEvent.class new file mode 100644 index 0000000..f8a1313 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutEvent.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutListener.class b/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutListener.class new file mode 100644 index 0000000..4b01d67 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/event/TimeoutListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/Experiment.class b/image_annotation/bin/ie/dcu/apps/ist/exp/Experiment.class new file mode 100644 index 0000000..89c237e Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/Experiment.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentFactory.class b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentFactory.class new file mode 100644 index 0000000..91d7718 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentFactory.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentManager.class b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentManager.class new file mode 100644 index 0000000..d9f713a Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentManager.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentResults.class b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentResults.class new file mode 100644 index 0000000..4d70115 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/ExperimentResults.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/FormatException.class b/image_annotation/bin/ie/dcu/apps/ist/exp/FormatException.class new file mode 100644 index 0000000..283a99a Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/FormatException.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/StorageSelection.class b/image_annotation/bin/ie/dcu/apps/ist/exp/StorageSelection.class new file mode 100644 index 0000000..ade3ece Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/StorageSelection.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/exp/Task.class b/image_annotation/bin/ie/dcu/apps/ist/exp/Task.class new file mode 100644 index 0000000..24bbccd Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/exp/Task.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/AreaShape.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/AreaShape.class new file mode 100644 index 0000000..2b78c0b Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/AreaShape.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ExportException.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ExportException.class new file mode 100644 index 0000000..c0070b3 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ExportException.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/Exporter.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/Exporter.class new file mode 100644 index 0000000..65b56fb Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/Exporter.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/HtmlTag.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/HtmlTag.class new file mode 100644 index 0000000..fcbd5f6 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/HtmlTag.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ImageMap.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ImageMap.class new file mode 100644 index 0000000..712d59f Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/ImageMap.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/MapArea.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/MapArea.class new file mode 100644 index 0000000..0988502 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/MapArea.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$BrightenForeground.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$BrightenForeground.class new file mode 100644 index 0000000..36e78d2 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$BrightenForeground.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$DarkenBackground.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$DarkenBackground.class new file mode 100644 index 0000000..ba2d35a Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$DarkenBackground.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$OutlineObject.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$OutlineObject.class new file mode 100644 index 0000000..17576cd Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect$OutlineObject.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect.class b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect.class new file mode 100644 index 0000000..bfeb640 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/RolloverEffect.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/template.html b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/template.html new file mode 100644 index 0000000..159d2a1 --- /dev/null +++ b/image_annotation/bin/ie/dcu/apps/ist/export/imagemap/template.html @@ -0,0 +1,38 @@ + + + + + + + + ${page-title} + + + + + + + ${image-alt} + + + +${contents} + + + \ No newline at end of file diff --git a/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles$1.class b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles$1.class new file mode 100644 index 0000000..2404137 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles.class b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles.class new file mode 100644 index 0000000..bf2d162 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFiles.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesEvent.class b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesEvent.class new file mode 100644 index 0000000..e1b6618 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesEvent.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesListener.class b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesListener.class new file mode 100644 index 0000000..b1b7d91 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/recent/RecentFilesListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/EvaluationListener.class b/image_annotation/bin/ie/dcu/apps/ist/views/EvaluationListener.class new file mode 100644 index 0000000..6c4a328 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/EvaluationListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$1.class b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$1.class new file mode 100644 index 0000000..b068920 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$2.class b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$2.class new file mode 100644 index 0000000..159a197 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$3.class b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$3.class new file mode 100644 index 0000000..ada5e53 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$ButtonType.class b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$ButtonType.class new file mode 100644 index 0000000..34c12e3 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel$ButtonType.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel.class b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel.class new file mode 100644 index 0000000..c1696f1 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/ExperimentPanel.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$1.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$1.class new file mode 100644 index 0000000..bf56677 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$2.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$2.class new file mode 100644 index 0000000..49f29da Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$3.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$3.class new file mode 100644 index 0000000..dd3a873 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$4.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$4.class new file mode 100644 index 0000000..3b8e206 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$4.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$EventHandler.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$EventHandler.class new file mode 100644 index 0000000..7038114 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$EventHandler.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$RobustSegmenterProxy.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$RobustSegmenterProxy.class new file mode 100644 index 0000000..f37c2f5 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$RobustSegmenterProxy.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$Tool.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$Tool.class new file mode 100644 index 0000000..c68788c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$Tool.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$ToolAction.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$ToolAction.class new file mode 100644 index 0000000..ae331cf Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView$ToolAction.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView.class b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView.class new file mode 100644 index 0000000..2c3e22e Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/views/SegmentationView.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$1.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$1.class new file mode 100644 index 0000000..0b5d730 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$2.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$2.class new file mode 100644 index 0000000..01fdb01 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$3.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$3.class new file mode 100644 index 0000000..f6419a2 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl.class new file mode 100644 index 0000000..5448699 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/AnnotatedImageControl.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl$ScaleChangeListener.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl$ScaleChangeListener.class new file mode 100644 index 0000000..81476d4 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl$ScaleChangeListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl.class new file mode 100644 index 0000000..b8dbf7d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/BrushControl.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector$ControlListener.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector$ControlListener.class new file mode 100644 index 0000000..105547c Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector$ControlListener.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector.class new file mode 100644 index 0000000..afad48d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/ColorSelector.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/ImageMenuManager.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/ImageMenuManager.class new file mode 100644 index 0000000..aea558d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/ImageMenuManager.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$1.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$1.class new file mode 100644 index 0000000..e6d8d99 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$1.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$2.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$2.class new file mode 100644 index 0000000..8ef114d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$2.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$3.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$3.class new file mode 100644 index 0000000..42e939d Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$3.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$4.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$4.class new file mode 100644 index 0000000..cc64dd0 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$4.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$State.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$State.class new file mode 100644 index 0000000..d4391c9 Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer$State.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer.class new file mode 100644 index 0000000..0fab82f Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/SwtTimer.class differ diff --git a/image_annotation/bin/ie/dcu/apps/ist/widgets/Ticker.class b/image_annotation/bin/ie/dcu/apps/ist/widgets/Ticker.class new file mode 100644 index 0000000..e95c65e Binary files /dev/null and b/image_annotation/bin/ie/dcu/apps/ist/widgets/Ticker.class differ diff --git a/image_annotation/build.xml b/image_annotation/build.xml new file mode 100644 index 0000000..e6b752a --- /dev/null +++ b/image_annotation/build.xml @@ -0,0 +1,402 @@ + + + + + The interactive segmentation tool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A tool that allows you to interactively extract an object + from an image using a variety of scribble based algorithms + + + + + + + + + + + + + + + + + A tool that allows you to interactively extract an object + from an image using a variety of scribble based algorithms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/image_annotation/lib/._swt-cocoa-32.jar b/image_annotation/lib/._swt-cocoa-32.jar new file mode 100644 index 0000000..aca5ab2 Binary files /dev/null and b/image_annotation/lib/._swt-cocoa-32.jar differ diff --git a/image_annotation/lib/._swt-cocoa-64.jar b/image_annotation/lib/._swt-cocoa-64.jar new file mode 100644 index 0000000..90875d7 Binary files /dev/null and b/image_annotation/lib/._swt-cocoa-64.jar differ diff --git a/image_annotation/lib/.classpath b/image_annotation/lib/.classpath new file mode 100644 index 0000000..4027147 --- /dev/null +++ b/image_annotation/lib/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/image_annotation/lib/.project b/image_annotation/lib/.project new file mode 100644 index 0000000..9f09891 --- /dev/null +++ b/image_annotation/lib/.project @@ -0,0 +1,17 @@ + + + TestAnn + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/image_annotation/lib/istapi-doc.zip b/image_annotation/lib/istapi-doc.zip new file mode 100644 index 0000000..90a6377 Binary files /dev/null and b/image_annotation/lib/istapi-doc.zip differ diff --git a/image_annotation/lib/istapi-src.jar b/image_annotation/lib/istapi-src.jar new file mode 100644 index 0000000..63dd31d Binary files /dev/null and b/image_annotation/lib/istapi-src.jar differ diff --git a/image_annotation/lib/istapi.jar b/image_annotation/lib/istapi.jar new file mode 100644 index 0000000..4ad357a Binary files /dev/null and b/image_annotation/lib/istapi.jar differ diff --git a/image_annotation/lib/jface.jar b/image_annotation/lib/jface.jar new file mode 100644 index 0000000..f515a8f Binary files /dev/null and b/image_annotation/lib/jface.jar differ diff --git a/image_annotation/lib/swt-cocoa-32.jar b/image_annotation/lib/swt-cocoa-32.jar new file mode 100644 index 0000000..d42da95 Binary files /dev/null and b/image_annotation/lib/swt-cocoa-32.jar differ diff --git a/image_annotation/lib/swt-cocoa-64.jar b/image_annotation/lib/swt-cocoa-64.jar new file mode 100644 index 0000000..228da89 Binary files /dev/null and b/image_annotation/lib/swt-cocoa-64.jar differ diff --git a/image_annotation/lib/swt-gtk-64.jar b/image_annotation/lib/swt-gtk-64.jar new file mode 100644 index 0000000..4ce079f Binary files /dev/null and b/image_annotation/lib/swt-gtk-64.jar differ diff --git a/image_annotation/lib/swt-gtk.jar b/image_annotation/lib/swt-gtk.jar new file mode 100644 index 0000000..5c94f6e Binary files /dev/null and b/image_annotation/lib/swt-gtk.jar differ diff --git a/image_annotation/lib/swt-win.jar b/image_annotation/lib/swt-win.jar new file mode 100644 index 0000000..5eb6d90 Binary files /dev/null and b/image_annotation/lib/swt-win.jar differ diff --git a/image_annotation/plugins/BPTPlugin.plugin/bptplugin.jar b/image_annotation/plugins/BPTPlugin.plugin/bptplugin.jar new file mode 100644 index 0000000..d4025cf Binary files /dev/null and b/image_annotation/plugins/BPTPlugin.plugin/bptplugin.jar differ diff --git a/image_annotation/plugins/BPTPlugin.plugin/jbpt.dll b/image_annotation/plugins/BPTPlugin.plugin/jbpt.dll new file mode 100644 index 0000000..d12f777 Binary files /dev/null and b/image_annotation/plugins/BPTPlugin.plugin/jbpt.dll differ diff --git a/image_annotation/plugins/BPTPlugin.plugin/libjbpt.jnilib b/image_annotation/plugins/BPTPlugin.plugin/libjbpt.jnilib new file mode 100644 index 0000000..77039d7 Binary files /dev/null and b/image_annotation/plugins/BPTPlugin.plugin/libjbpt.jnilib differ diff --git a/image_annotation/plugins/BPTPlugin.plugin/plugin.xml b/image_annotation/plugins/BPTPlugin.plugin/plugin.xml new file mode 100644 index 0000000..4ae529d --- /dev/null +++ b/image_annotation/plugins/BPTPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/plugins/IGCPlugin.plugin/igcplugin.jar b/image_annotation/plugins/IGCPlugin.plugin/igcplugin.jar new file mode 100644 index 0000000..c85393b Binary files /dev/null and b/image_annotation/plugins/IGCPlugin.plugin/igcplugin.jar differ diff --git a/image_annotation/plugins/IGCPlugin.plugin/jigc.dll b/image_annotation/plugins/IGCPlugin.plugin/jigc.dll new file mode 100644 index 0000000..b4ee98c Binary files /dev/null and b/image_annotation/plugins/IGCPlugin.plugin/jigc.dll differ diff --git a/image_annotation/plugins/IGCPlugin.plugin/libjigc.jnilib b/image_annotation/plugins/IGCPlugin.plugin/libjigc.jnilib new file mode 100644 index 0000000..6ff9817 Binary files /dev/null and b/image_annotation/plugins/IGCPlugin.plugin/libjigc.jnilib differ diff --git a/image_annotation/plugins/IGCPlugin.plugin/plugin.xml b/image_annotation/plugins/IGCPlugin.plugin/plugin.xml new file mode 100644 index 0000000..2b7ae2e --- /dev/null +++ b/image_annotation/plugins/IGCPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/plugins/SIOXPlugin.plugin/plugin.xml b/image_annotation/plugins/SIOXPlugin.plugin/plugin.xml new file mode 100644 index 0000000..623ad7b --- /dev/null +++ b/image_annotation/plugins/SIOXPlugin.plugin/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/image_annotation/plugins/SIOXPlugin.plugin/sioxapi.jar b/image_annotation/plugins/SIOXPlugin.plugin/sioxapi.jar new file mode 100644 index 0000000..4c121b2 Binary files /dev/null and b/image_annotation/plugins/SIOXPlugin.plugin/sioxapi.jar differ diff --git a/image_annotation/plugins/SIOXPlugin.plugin/sioxplugin.jar b/image_annotation/plugins/SIOXPlugin.plugin/sioxplugin.jar new file mode 100644 index 0000000..31f959a Binary files /dev/null and b/image_annotation/plugins/SIOXPlugin.plugin/sioxplugin.jar differ diff --git a/image_annotation/plugins/SRGPlugin.plugin/jsrg.dll b/image_annotation/plugins/SRGPlugin.plugin/jsrg.dll new file mode 100644 index 0000000..c381fae Binary files /dev/null and b/image_annotation/plugins/SRGPlugin.plugin/jsrg.dll differ diff --git a/image_annotation/plugins/SRGPlugin.plugin/libjsrg.jnilib b/image_annotation/plugins/SRGPlugin.plugin/libjsrg.jnilib new file mode 100644 index 0000000..46427e8 Binary files /dev/null and b/image_annotation/plugins/SRGPlugin.plugin/libjsrg.jnilib differ diff --git a/image_annotation/plugins/SRGPlugin.plugin/plugin.xml b/image_annotation/plugins/SRGPlugin.plugin/plugin.xml new file mode 100644 index 0000000..4061f98 --- /dev/null +++ b/image_annotation/plugins/SRGPlugin.plugin/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image_annotation/plugins/SRGPlugin.plugin/srgplugin.jar b/image_annotation/plugins/SRGPlugin.plugin/srgplugin.jar new file mode 100644 index 0000000..c888c62 Binary files /dev/null and b/image_annotation/plugins/SRGPlugin.plugin/srgplugin.jar differ diff --git a/image_annotation/resources/ant/lib/ant-deb.jar b/image_annotation/resources/ant/lib/ant-deb.jar new file mode 100644 index 0000000..f602bec Binary files /dev/null and b/image_annotation/resources/ant/lib/ant-deb.jar differ diff --git a/image_annotation/resources/ant/lib/ant-gzip2.jar b/image_annotation/resources/ant/lib/ant-gzip2.jar new file mode 100644 index 0000000..271f123 Binary files /dev/null and b/image_annotation/resources/ant/lib/ant-gzip2.jar differ diff --git a/image_annotation/resources/ant/lib/ant-passwd-task.jar b/image_annotation/resources/ant/lib/ant-passwd-task.jar new file mode 100644 index 0000000..b4f4b76 Binary files /dev/null and b/image_annotation/resources/ant/lib/ant-passwd-task.jar differ diff --git a/image_annotation/resources/ant/lib/ant-swt-identify.jar b/image_annotation/resources/ant/lib/ant-swt-identify.jar new file mode 100644 index 0000000..daddc1c Binary files /dev/null and b/image_annotation/resources/ant/lib/ant-swt-identify.jar differ diff --git a/image_annotation/resources/ant/lib/jsmoothgen-ant.jar b/image_annotation/resources/ant/lib/jsmoothgen-ant.jar new file mode 100644 index 0000000..c9e283d Binary files /dev/null and b/image_annotation/resources/ant/lib/jsmoothgen-ant.jar differ diff --git a/image_annotation/resources/ant/lib/standalone-compiler.jar b/image_annotation/resources/ant/lib/standalone-compiler.jar new file mode 100644 index 0000000..2762975 Binary files /dev/null and b/image_annotation/resources/ant/lib/standalone-compiler.jar differ diff --git a/image_annotation/resources/ant/properties b/image_annotation/resources/ant/properties new file mode 100644 index 0000000..38b429c --- /dev/null +++ b/image_annotation/resources/ant/properties @@ -0,0 +1,14 @@ +app.name=Interactive Segmentation Tool +app.package=ist +app.version=1.3.4 +app.author=Kevin McGuinness +app.email=kevin.mcguinness@eeng.dcu.ie +app.website=http://kspace.cdvp.dcu.ie/public/interactive-segmentation +app.synopsis=The CDVP Interactive Segmentation Tool +app.main=ie.dcu.apps.ist.Main + +# Compilation properties +java.debug=off +java.source.version=1.5 +java.target.version=1.5 + diff --git a/image_annotation/resources/config/actions.mac.properties b/image_annotation/resources/config/actions.mac.properties new file mode 100644 index 0000000..8a282fa --- /dev/null +++ b/image_annotation/resources/config/actions.mac.properties @@ -0,0 +1,108 @@ + +OpenAction.dialog.text=Open an image or saved context +OpenAction.dialog.filter.exts=*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.ctx +OpenAction.dialog.filter.text=Image and Context Files + +SaveAction.dialog.text=Save Segmentation Context +SaveAction.dialog.filter.exts=*.ctx +SaveAction.dialog.filter.text=Segmentation Context Files + +ExportViewAction.dialog.text=Export View As Image +ExportViewAction.dialog.filter.exts=*.jpg;*.jpeg;*.png;*.gif;*.bmp; +ExportViewAction.dialog.filter.text=Image Files + +OpenExperimentAction.dialog.text=Open an experiment file +OpenExperimentAction.dialog.filter.exts=*.exp +OpenExperimentAction.dialog.filter.text=Experiment Files + +# Actions +action.OpenAction.text=&Open...@Command+O +action.OpenAction.image=file:resources/icons/open.png +action.OpenAction.tooltip=Open image + +action.SaveAction.text=&Save@Command+S +action.SaveAction.image=file:resources/icons/save.png +action.SaveAction.tooltip=Save segmentation context + +action.SaveAsAction.text=S&ave As...@Command+Shift+S +action.SaveAsAction.image=file:resources/icons/save-as.png +action.SaveAsAction.tooltip=Save segmentation context as... + +action.ExportViewAction.text=&Current View...@Command+Alt+V +action.ExportViewAction.image=file:resources/icons/image.png +action.ExportViewAction.tooltip=Export view as image + +action.ExportImageMapAction.text=&HTML Image Map...@Command+Alt+H +action.ExportImageMapAction.image=file:resources/icons/html.png +action.ExportImageMapAction.tooltip=Export objects as HTML image map + +action.ExportTransparentPNGAction.text=Export Transparent &PNG...@Command+Alt+P +action.ExportTransparentPNGAction.image=file:resources/icons/image.png +action.ExportTransparentPNGAction.tooltip=Export a PNG of the current segmentation with a transparent background + +action.ImportAction.text=&Import... +action.ImportAction.image=file:resources/icons/import.png +action.ImportAction.tooltip=Import images + +action.PrintAction.text=&Print@Command+P +action.PrintAction.image=file:resources/icons/print.png +action.PrintAction.tooltip=Print view + +action.ExitAction.text=E&xit@Command+Q +action.ExitAction.image=file:resources/icons/exit.png +action.ExitAction.tooltip=Quit the application + +action.UndoAction.text=&Undo@Command+Z +action.UndoAction.image=file:resources/icons/undo.png +action.UndoAction.tooltip=Undo last markup + +action.RedoAction.text=&Redo@Command+Y +action.RedoAction.image=file:resources/icons/redo.png +action.RedoAction.tooltip=Redo markup + +action.CopyAction.text=&Copy@Command+C +action.CopyAction.image=file:resources/icons/copy.png +action.CopyAction.tooltip=Copy view to clipboard as image + +action.PreferencesAction.text=&Preferences...@Command+, +action.PreferencesAction.image=file:resources/icons/preferences.png +action.PreferencesAction.tooltip=Change application settings + +action.AddFilesAction.text=&Add Files...@Command+A +action.AddFilesAction.image=file:resources/icons/add.png +action.AddFilesAction.tooltip=Add image files to project + +action.RemoveFilesAction.text=&Remove Files@Command+R +action.RemoveFilesAction.image=file:resources/icons/remove.png +action.RemoveFilesAction.tooltip=Remove selected image files from project + +action.PreviousAction.text=&Previous@Command+[ +action.PreviousAction.image=file:resources/icons/previous.png +action.PreviousAction.tooltip=Move to previous image in directory + +action.NextAction.text=&Next@Command+] +action.NextAction.image=file:resources/icons/next.png +action.NextAction.tooltip=Move to next image in directory + +action.HelpAction.text=&Help Contents...@Command+F1 +action.HelpAction.image=file:resources/icons/help.png +action.HelpAction.tooltip=Show application help browser +action.HelpAction.helpURL=http://kspace.cdvp.dcu.ie/public/interactive-segmentation/doc/help.html + +action.AboutAction.text=&About... +action.AboutAction.image=file:resources/icons/about.png +action.AboutAction.tooltip=About the application +action.AboutAction.aboutImage=file:resources/icons/about-box.png + +action.OpenExperimentAction.text=Open &Experiment... +action.OpenExperimentAction.image=file:resources/icons/experiment.png +action.OpenExperimentAction.tooltip=Open an experiment file and switch to experiment mode + +action.ConfigureSegmenterAction.text=&Configure Segmenter...@Command+Shift+C +action.ConfigureSegmenterAction.image=file:resources/icons/preferences.png +action.ConfigureSegmenterAction.tooltip=Configure segmenter parameters + +action.SelectSegmenterAction.NA=Algorithm not available on this platform + + + diff --git a/image_annotation/resources/config/actions.properties b/image_annotation/resources/config/actions.properties new file mode 100644 index 0000000..f2ddcb0 --- /dev/null +++ b/image_annotation/resources/config/actions.properties @@ -0,0 +1,106 @@ + +OpenAction.dialog.text=Open an image or saved context +OpenAction.dialog.filter.exts=*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.ctx +OpenAction.dialog.filter.text=Image and Context Files + +SaveAction.dialog.text=Save Segmentation Context +SaveAction.dialog.filter.exts=*.ctx +SaveAction.dialog.filter.text=Segmentation Context Files + +ExportViewAction.dialog.text=Export View As Image +ExportViewAction.dialog.filter.exts=*.jpg;*.jpeg;*.png;*.gif;*.bmp; +ExportViewAction.dialog.filter.text=Image Files + +OpenExperimentAction.dialog.text=Open an experiment file +OpenExperimentAction.dialog.filter.exts=*.exp +OpenExperimentAction.dialog.filter.text=Experiment Files + +# Actions +action.OpenAction.text=&Open...@Ctrl+O +action.OpenAction.image=file:resources/icons/open.png +action.OpenAction.tooltip=Open image + +action.SaveAction.text=&Save@Ctrl+S +action.SaveAction.image=file:resources/icons/save.png +action.SaveAction.tooltip=Save segmentation context + +action.SaveAsAction.text=S&ave As...@Ctrl+Shift+S +action.SaveAsAction.image=file:resources/icons/save-as.png +action.SaveAsAction.tooltip=Save segmentation context as... + +action.ExportViewAction.text=&Current View...@Ctrl+Shift+V +action.ExportViewAction.image=file:resources/icons/image.png +action.ExportViewAction.tooltip=Export view as image + +action.ExportImageMapAction.text=&HTML Image Map...@Ctrl+Shift+H +action.ExportImageMapAction.image=file:resources/icons/html.png +action.ExportImageMapAction.tooltip=Export objects as HTML image map + +action.ExportTransparentPNGAction.text=Export Transparent &PNG...@Ctrl+Shift+P +action.ExportTransparentPNGAction.image=file:resources/icons/image.png +action.ExportTransparentPNGAction.tooltip=Export a PNG of the current segmentation with a transparent background + + +action.ImportAction.text=&Import... +action.ImportAction.image=file:resources/icons/import.png +action.ImportAction.tooltip=Import images + +action.PrintAction.text=&Print@Ctrl+P +action.PrintAction.image=file:resources/icons/print.png +action.PrintAction.tooltip=Print view + +action.ExitAction.text=E&xit@Ctrl+Q +action.ExitAction.image=file:resources/icons/exit.png +action.ExitAction.tooltip=Quit the application + +action.UndoAction.text=&Undo@Ctrl+Z +action.UndoAction.image=file:resources/icons/undo.png +action.UndoAction.tooltip=Undo last markup + +action.RedoAction.text=&Redo@Ctrl+Y +action.RedoAction.image=file:resources/icons/redo.png +action.RedoAction.tooltip=Redo markup + +action.CopyAction.text=&Copy@Ctrl+C +action.CopyAction.image=file:resources/icons/copy.png +action.CopyAction.tooltip=Copy view to clipboard as image + +action.PreferencesAction.text=&Preferences... +action.PreferencesAction.image=file:resources/icons/preferences.png +action.PreferencesAction.tooltip=Change application settings + +action.AddFilesAction.text=&Add Files...@Ctrl+A +action.AddFilesAction.image=file:resources/icons/add.png +action.AddFilesAction.tooltip=Add image files to project + +action.RemoveFilesAction.text=&Remove Files@Ctrl+R +action.RemoveFilesAction.image=file:resources/icons/remove.png +action.RemoveFilesAction.tooltip=Remove selected image files from project + +action.PreviousAction.text=&Previous@Ctrl+[ +action.PreviousAction.image=file:resources/icons/previous.png +action.PreviousAction.tooltip=Move to previous image in directory + +action.NextAction.text=&Next@Ctrl+] +action.NextAction.image=file:resources/icons/next.png +action.NextAction.tooltip=Move to next image in directory + +action.HelpAction.text=&Help Contents...@Ctrl+F1 +action.HelpAction.image=file:resources/icons/help.png +action.HelpAction.tooltip=Show application help browser +action.HelpAction.helpURL=http://kspace.cdvp.dcu.ie/public/interactive-segmentation/doc/help.html + +action.AboutAction.text=&About... +action.AboutAction.image=file:resources/icons/about.png +action.AboutAction.tooltip=About the application +action.AboutAction.aboutImage=file:resources/icons/about-box.png + +action.OpenExperimentAction.text=Open &Experiment... +action.OpenExperimentAction.image=file:resources/icons/experiment.png +action.OpenExperimentAction.tooltip=Open an experiment file and switch to experiment mode + +action.ConfigureSegmenterAction.text=&Configure Segmenter...@Ctrl+Shift+C +action.ConfigureSegmenterAction.image=file:resources/icons/preferences.png +action.ConfigureSegmenterAction.tooltip=Configure segmenter parameters + +action.SelectSegmenterAction.NA=Algorithm not available on this platform diff --git a/image_annotation/resources/config/application.mac.properties b/image_annotation/resources/config/application.mac.properties new file mode 100644 index 0000000..096ea68 --- /dev/null +++ b/image_annotation/resources/config/application.mac.properties @@ -0,0 +1,25 @@ + +AppRecentMenu.icon=file:resources/icons/open-recent.png +ExportMenu.icon=file:resources/icons/export.png + +# Preferences Dialog +PrefsDialog.title=Preferences +PrefsDialog.application=Application +PrefsDialog.status=Status Area +PrefsDialog.view=View Preferences +PrefsDialog.experiment=Experiment Mode +PrefsDialog.general.tab=General +PrefsDialog.close.text=&Close +PrefsDialog.reset.text=&Reset +PrefsDialog.enable-feedback.text=Show position and color in the status area +PrefsDialog.foreground-color.text=Foreground color for markup +PrefsDialog.background-color.text=Background color for markup +PrefsDialog.experiment-embedded.text=Show experiment panel embedded in main window +PrefsDialog.confirm-exit.text=Confirm exit when application is closed + +# Experiment Panel +ExperimentPanel.description.title=Task Description +ExperimentPanel.timeout-message=The time allocated for this task is up! +ExperimentPanel.experiment-complete-message=The experiment has been completed +ExperimentPanel.button.text.start=Start +ExperimentPanel.button.text.finish=Finish diff --git a/image_annotation/resources/config/application.properties b/image_annotation/resources/config/application.properties new file mode 100644 index 0000000..d21c608 --- /dev/null +++ b/image_annotation/resources/config/application.properties @@ -0,0 +1,27 @@ + +AppRecentMenu.icon=file:resources/icons/open-recent.png +ExportMenu.icon=file:resources/icons/export.png + +# Preferences Dialog +PrefsDialog.title=Preferences +PrefsDialog.application=Application +PrefsDialog.status=Status Area +PrefsDialog.view=View Preferences +PrefsDialog.experiment=Experiment Mode +PrefsDialog.general.tab=General +PrefsDialog.close.text=&Close +PrefsDialog.close.icon=file:resources/icons/close.png +PrefsDialog.reset.text=&Reset +PrefsDialog.reset.icon=file:resources/icons/reset.png +PrefsDialog.enable-feedback.text=Show position and color in the status area +PrefsDialog.foreground-color.text=Foreground color for markup +PrefsDialog.background-color.text=Background color for markup +PrefsDialog.experiment-embedded.text=Show experiment panel embedded in main window +PrefsDialog.confirm-exit.text=Confirm exit when application is closed + +# Experiment Panel +ExperimentPanel.description.title=Task Description +ExperimentPanel.timeout-message=The time allocated for this task is up! +ExperimentPanel.experiment-complete-message=The experiment has been completed +ExperimentPanel.button.text.start=Start +ExperimentPanel.button.text.finish=Finish diff --git a/image_annotation/resources/config/view.properties b/image_annotation/resources/config/view.properties new file mode 100644 index 0000000..73be3a2 --- /dev/null +++ b/image_annotation/resources/config/view.properties @@ -0,0 +1,49 @@ +SegmentationView.Action.Foreground.text=Markup Foreground +SegmentationView.Action.Foreground.tooltip=Markup Foreground Pixels +SegmentationView.Action.Foreground.image=file:resources/icons/foreground.png +SegmentationView.Action.Background.text=Markup Background +SegmentationView.Action.Background.tooltip=Markup Background Pixels +SegmentationView.Action.Background.image=file:resources/icons/background.png +SegmentationView.Action.ZoomIn.text=Zoom In +SegmentationView.Action.ZoomIn.tooltip=Zoom In +SegmentationView.Action.ZoomIn.image=file:resources/icons/zoom-in.png +SegmentationView.Action.ZoomOut.text=Zoom Out +SegmentationView.Action.ZoomOut.tooltip=Zoom Out +SegmentationView.Action.ZoomOut.image=file:resources/icons/zoom-out.png +SegmentationView.Action.ZoomOriginal.text=Zoom Original +SegmentationView.Action.ZoomOriginal.tooltip=Zoom to Original Size +SegmentationView.Action.ZoomOriginal.image=file:resources/icons/zoom-original.png +SegmentationView.Action.ZoomBestFit.text=Zoom Best Fit +SegmentationView.Action.ZoomBestFit.tooltip=Zoom to Best Fit +SegmentationView.Action.ZoomBestFit.image=file:resources/icons/zoom-best-fit.png +SegmentationView.Action.Repaint.text=Refresh +SegmentationView.Action.Repaint.tooltip=Refresh and repaint view +SegmentationView.Action.Repaint.image=file:resources/icons/refresh.png +SegmentationView.Action.Undo.text=&Undo@Ctrl+Z +SegmentationView.Action.Undo.tooltip=Undo last markup +SegmentationView.Action.Undo.image=file:resources/icons/undo.png +SegmentationView.Action.Redo.text=&Redo@Ctrl+Y +SegmentationView.Action.Redo.tooltip=Redo markup +SegmentationView.Action.Redo.image=file:resources/icons/redo.png +SegmentationView.Action.Clear.text=&Clear@Ctrl+DELETE +SegmentationView.Action.Clear.tooltip=Clear all markup +SegmentationView.Action.Clear.image=file:resources/icons/clear.png +SegmentationView.Action.SetBrushSize.text=Set &Brush@Ctrl+B +SegmentationView.Action.SetBrushSize.tooltip=Set the markup brush size +SegmentationView.Action.SetBrushSize.image=file:resources/icons/brush.png +SegmentationView.Action.AutoApply.text=Auto Segment@Ctrl+Shift+A +SegmentationView.Action.AutoApply.tooltip=Toggle auto segment +SegmentationView.Action.AutoApply.image=file:resources/icons/auto.png +SegmentationView.Action.Apply.text=Segment@Ctrl+Shift+S +SegmentationView.Action.Apply.tooltip=Apply new markup now +SegmentationView.Action.Apply.image=file:resources/icons/apply.png +SegmentationView.Action.SetPainter.text=View : +SegmentationView.Action.SetPainter.tooltip=Select how to view the image/segmentation. +SegmentationView.Action.SetPainter.image= +SegmentationView.Action.SegmenterOptions.text=Configure Segmenter@Ctrl+Shift+C +SegmentationView.Action.SegmenterOptions.tooltip=Configure the segmenter +SegmentationView.Action.SegmenterOptions.image=file:resources/icons/preferences.png +SegmentationView.Action.SetLabel.text=Annotate : +SegmentationView.Action.SetLabel.tooltip=Annotate the segmented piece. +SegmentationView.Action.SetLabel.image= + diff --git a/image_annotation/resources/doc/help.html b/image_annotation/resources/doc/help.html new file mode 100644 index 0000000..44a573f --- /dev/null +++ b/image_annotation/resources/doc/help.html @@ -0,0 +1,169 @@ + +Interactive Segmentation Tool - User Guide + +
+

Interactive Segmentation Tool

+

User Guide

+

Usage

+In this section I will outline how to perform typical operations using +the tool, such as opening images, extracting objects and exporting +segmentation masks.
+
+Open an Image 
+To open an image, select File->Open. Recently open files can +be accessed using the File->Open +Recent menu. Alternatively, an image file can be dragged +from your file system and dropped into the application to open it. +Currently images of type JPEG, PNG, GIF, and BMP are supported.
+
+Select a Segmenter
+There are several different segmenters available in the tool. These can +be selected from the Tools +menu. To select one, +click on it.
+
+Segmenting the Image 
+Once you have an image open and a segmenter selected, you can segment +the image by drawing scribbles on the image. First, move the mouse over +the object you want to extract. Left click and drag inside the object +to draw some scribbles inside the object. Now, move the mouse outside +the object to some background pixels. Using the right mouse button, +scribble outside the object to select some background. If using Mac +OSX, or if you do not have a right mouse button (Graphics Tablet etc), +you can use Ctrl +and click instead of right click.
+
+The image will be segmented and the foreground will be brightened and +the background darkened. If you are not happy with the segmentation, +you can add more foreground and background scribbles.
+
+Undo, Redo and Clear
+If you make a mistake, it can be undone by selecting Edit->Undo or +clicking the undo Undo Button button in the +toolbar. To redo, select Edit->Redo or click the +redo Redo Button button. To +remove all forground and background markup, click on the clear Clear Button button in the toolbar.
+
+Zooming
+To get a closer look at the image, the toolbar can also be +used to zoom-in on the image. To zoom-in by 10% use the Zoom In button. To zoom-out by 10% use +the Zoom Out button. To +zoom the image such that it fits into the window without needing +scrollbars, use the Zoom Best Fit +button. To restore the image to its original size use the Zoom Original button.
+
+Brush Size
+To change the brush size, use the Paintbrush button to show the brush size +chooser. It is often useful to use a bigger brush to make markings more +visible or to quickly mark up more pixels.
+
+Views
+It is sometimes useful to view the segmentation results in different +ways. The default selected view is called "Combined" and shows the +image, it's segmentation highlighted and the scribbles all overlayed on +the same image. By using the drop down menu, you can select a different +view:
+ +Note: +Only the combined view displays the foreground and +background markup (scribbles). So if you are wondering where the +scribbles disappeared to, select the combined view to make them visible.
+
+Saving and Exporting
+To save an work in progress segmentation, select File->Save or File->Save As. This +will save the current segmentation and markup as a context file (.ctx). When +finished segmenting an image, you may want to export the view as an +image. To export the current view select File->Export. +For example, if you wanted to save the segmentation mask, select the +mask view and then File->Export. +
+
+Configuring a Segmenter
+Some segmenters have parameters and options that can are configurable. +To configure the selected segmenter, select Tools->Configure +Segmenter, or click the Configure button in the toolbar.
+
+Navigating Directories of Images
+To quickly jump to the next or previous image in the current directory +(the one containing the open image), use the Go->Next and Go->Previous menu +items.
+
+Experiments
+Experiment files can be opened using the Tools->Open +Experiment menu item.


+ +
+ \ No newline at end of file diff --git a/image_annotation/resources/icons/about-box.png b/image_annotation/resources/icons/about-box.png new file mode 100644 index 0000000..3cd2db6 Binary files /dev/null and b/image_annotation/resources/icons/about-box.png differ diff --git a/image_annotation/resources/icons/about.png b/image_annotation/resources/icons/about.png new file mode 100644 index 0000000..3b8a885 Binary files /dev/null and b/image_annotation/resources/icons/about.png differ diff --git a/image_annotation/resources/icons/application.png b/image_annotation/resources/icons/application.png new file mode 100644 index 0000000..ca883da Binary files /dev/null and b/image_annotation/resources/icons/application.png differ diff --git a/image_annotation/resources/icons/apply.png b/image_annotation/resources/icons/apply.png new file mode 100644 index 0000000..d7b5c2b Binary files /dev/null and b/image_annotation/resources/icons/apply.png differ diff --git a/image_annotation/resources/icons/auto.png b/image_annotation/resources/icons/auto.png new file mode 100644 index 0000000..a3e8c31 Binary files /dev/null and b/image_annotation/resources/icons/auto.png differ diff --git a/image_annotation/resources/icons/background.png b/image_annotation/resources/icons/background.png new file mode 100644 index 0000000..0e633a3 Binary files /dev/null and b/image_annotation/resources/icons/background.png differ diff --git a/image_annotation/resources/icons/brush.png b/image_annotation/resources/icons/brush.png new file mode 100644 index 0000000..4bb955f Binary files /dev/null and b/image_annotation/resources/icons/brush.png differ diff --git a/image_annotation/resources/icons/clear.png b/image_annotation/resources/icons/clear.png new file mode 100644 index 0000000..1556dfe Binary files /dev/null and b/image_annotation/resources/icons/clear.png differ diff --git a/image_annotation/resources/icons/close.png b/image_annotation/resources/icons/close.png new file mode 100644 index 0000000..78b931f Binary files /dev/null and b/image_annotation/resources/icons/close.png differ diff --git a/image_annotation/resources/icons/copy.png b/image_annotation/resources/icons/copy.png new file mode 100644 index 0000000..585579a Binary files /dev/null and b/image_annotation/resources/icons/copy.png differ diff --git a/image_annotation/resources/icons/dialog-error.png b/image_annotation/resources/icons/dialog-error.png new file mode 100644 index 0000000..c2d0bb7 Binary files /dev/null and b/image_annotation/resources/icons/dialog-error.png differ diff --git a/image_annotation/resources/icons/dialog-information.png b/image_annotation/resources/icons/dialog-information.png new file mode 100644 index 0000000..23824bb Binary files /dev/null and b/image_annotation/resources/icons/dialog-information.png differ diff --git a/image_annotation/resources/icons/dialog-warning.png b/image_annotation/resources/icons/dialog-warning.png new file mode 100644 index 0000000..06148e8 Binary files /dev/null and b/image_annotation/resources/icons/dialog-warning.png differ diff --git a/image_annotation/resources/icons/exit.png b/image_annotation/resources/icons/exit.png new file mode 100644 index 0000000..22a65a4 Binary files /dev/null and b/image_annotation/resources/icons/exit.png differ diff --git a/image_annotation/resources/icons/experiment.png b/image_annotation/resources/icons/experiment.png new file mode 100644 index 0000000..cb16a60 Binary files /dev/null and b/image_annotation/resources/icons/experiment.png differ diff --git a/image_annotation/resources/icons/export.png b/image_annotation/resources/icons/export.png new file mode 100644 index 0000000..f64425b Binary files /dev/null and b/image_annotation/resources/icons/export.png differ diff --git a/image_annotation/resources/icons/foreground.png b/image_annotation/resources/icons/foreground.png new file mode 100644 index 0000000..4161f1d Binary files /dev/null and b/image_annotation/resources/icons/foreground.png differ diff --git a/image_annotation/resources/icons/help.png b/image_annotation/resources/icons/help.png new file mode 100644 index 0000000..8542495 Binary files /dev/null and b/image_annotation/resources/icons/help.png differ diff --git a/image_annotation/resources/icons/html.png b/image_annotation/resources/icons/html.png new file mode 100644 index 0000000..d95fcc0 Binary files /dev/null and b/image_annotation/resources/icons/html.png differ diff --git a/image_annotation/resources/icons/icon-16.png b/image_annotation/resources/icons/icon-16.png new file mode 100644 index 0000000..b9e1264 Binary files /dev/null and b/image_annotation/resources/icons/icon-16.png differ diff --git a/image_annotation/resources/icons/icon-24.png b/image_annotation/resources/icons/icon-24.png new file mode 100644 index 0000000..7b93ba1 Binary files /dev/null and b/image_annotation/resources/icons/icon-24.png differ diff --git a/image_annotation/resources/icons/icon-32.png b/image_annotation/resources/icons/icon-32.png new file mode 100644 index 0000000..4bc8f32 Binary files /dev/null and b/image_annotation/resources/icons/icon-32.png differ diff --git a/image_annotation/resources/icons/icon-48.png b/image_annotation/resources/icons/icon-48.png new file mode 100644 index 0000000..c3fca3e Binary files /dev/null and b/image_annotation/resources/icons/icon-48.png differ diff --git a/image_annotation/resources/icons/image.png b/image_annotation/resources/icons/image.png new file mode 100644 index 0000000..247ccad Binary files /dev/null and b/image_annotation/resources/icons/image.png differ diff --git a/image_annotation/resources/icons/next.png b/image_annotation/resources/icons/next.png new file mode 100644 index 0000000..0011e67 Binary files /dev/null and b/image_annotation/resources/icons/next.png differ diff --git a/image_annotation/resources/icons/open-recent.png b/image_annotation/resources/icons/open-recent.png new file mode 100644 index 0000000..07cca3f Binary files /dev/null and b/image_annotation/resources/icons/open-recent.png differ diff --git a/image_annotation/resources/icons/open.png b/image_annotation/resources/icons/open.png new file mode 100644 index 0000000..c3bcfcd Binary files /dev/null and b/image_annotation/resources/icons/open.png differ diff --git a/image_annotation/resources/icons/preferences.png b/image_annotation/resources/icons/preferences.png new file mode 100644 index 0000000..e460eab Binary files /dev/null and b/image_annotation/resources/icons/preferences.png differ diff --git a/image_annotation/resources/icons/previous.png b/image_annotation/resources/icons/previous.png new file mode 100644 index 0000000..d3bc514 Binary files /dev/null and b/image_annotation/resources/icons/previous.png differ diff --git a/image_annotation/resources/icons/print.png b/image_annotation/resources/icons/print.png new file mode 100644 index 0000000..2f1a0ff Binary files /dev/null and b/image_annotation/resources/icons/print.png differ diff --git a/image_annotation/resources/icons/redo.png b/image_annotation/resources/icons/redo.png new file mode 100644 index 0000000..57de992 Binary files /dev/null and b/image_annotation/resources/icons/redo.png differ diff --git a/image_annotation/resources/icons/refresh.png b/image_annotation/resources/icons/refresh.png new file mode 100644 index 0000000..e46da27 Binary files /dev/null and b/image_annotation/resources/icons/refresh.png differ diff --git a/image_annotation/resources/icons/reset.png b/image_annotation/resources/icons/reset.png new file mode 100644 index 0000000..1556dfe Binary files /dev/null and b/image_annotation/resources/icons/reset.png differ diff --git a/image_annotation/resources/icons/run.png b/image_annotation/resources/icons/run.png new file mode 100644 index 0000000..9010e7a Binary files /dev/null and b/image_annotation/resources/icons/run.png differ diff --git a/image_annotation/resources/icons/save-as.png b/image_annotation/resources/icons/save-as.png new file mode 100644 index 0000000..a838052 Binary files /dev/null and b/image_annotation/resources/icons/save-as.png differ diff --git a/image_annotation/resources/icons/save.png b/image_annotation/resources/icons/save.png new file mode 100644 index 0000000..7da1d7c Binary files /dev/null and b/image_annotation/resources/icons/save.png differ diff --git a/image_annotation/resources/icons/undo.png b/image_annotation/resources/icons/undo.png new file mode 100644 index 0000000..48db6f2 Binary files /dev/null and b/image_annotation/resources/icons/undo.png differ diff --git a/image_annotation/resources/icons/view.png b/image_annotation/resources/icons/view.png new file mode 100644 index 0000000..68da502 Binary files /dev/null and b/image_annotation/resources/icons/view.png differ diff --git a/image_annotation/resources/icons/zoom-best-fit.png b/image_annotation/resources/icons/zoom-best-fit.png new file mode 100644 index 0000000..eb28409 Binary files /dev/null and b/image_annotation/resources/icons/zoom-best-fit.png differ diff --git a/image_annotation/resources/icons/zoom-in.png b/image_annotation/resources/icons/zoom-in.png new file mode 100644 index 0000000..31ac736 Binary files /dev/null and b/image_annotation/resources/icons/zoom-in.png differ diff --git a/image_annotation/resources/icons/zoom-original.png b/image_annotation/resources/icons/zoom-original.png new file mode 100644 index 0000000..8e35414 Binary files /dev/null and b/image_annotation/resources/icons/zoom-original.png differ diff --git a/image_annotation/resources/icons/zoom-out.png b/image_annotation/resources/icons/zoom-out.png new file mode 100644 index 0000000..df5be3c Binary files /dev/null and b/image_annotation/resources/icons/zoom-out.png differ diff --git a/image_annotation/resources/plaf/debian/changelog b/image_annotation/resources/plaf/debian/changelog new file mode 100644 index 0000000..7036b2c --- /dev/null +++ b/image_annotation/resources/plaf/debian/changelog @@ -0,0 +1,169 @@ +ist (1.3.4-1) stable; urgency=low + + * Support for Mac OS X leopard (64 bit JVM) + + * Fixed bug in transparent PNG export on Windows platforms + + -- Kevin McGuinness Thur, 27 Mar 2010 19:10:00 +0000 + +ist (1.3.3-1) stable; urgency=low + + * Updated API documentation + + * Larger dialog for segmenter options + + * Linux plugin builds now use GCC-4.1 for better compatibility + + -- Kevin McGuinness Wed, 7 Oct 2009 19:00:00 +0000 + +ist (1.3.2-1) stable; urgency=low + + * Moved more components to the API layer. This includes painters and the image + control. This allows other apps to share core components. + + * Added export transparent PNG option + + -- Kevin McGuinness Wed, 7 Oct 2009 18:00:00 +0000 + +ist (1.3.1-1) stable; urgency=low + + * Updated export HTML feature to export XHTML + + -- Kevin McGuinness Wed, 2 Sep 2009 18:00:00 +0000 + +ist (1.3.0-1) stable; urgency=low + + * Major restructuring of application to ease plugin development + + * New version of SWT + + -- Kevin McGuinness Mon, 24 Aug 2009 18:00:00 +0000 + +ist (1.2.5-1) stable; urgency=low + + * Added code for automated evaluation + + * Update man page and run templates + + -- Kevin McGuinness Mon, 9 Mar 2009 16:21:00 +0000 + +ist (1.2.4-1) stable; urgency=medium + + * Fixed erratic behavior of SIOX segmenter + + * Added a lot of code for automated evaluation + + * Added option to force segmenters to retain all markup pixels in mask. + + -- Kevin McGuinness Fri, 23 Jan 2009 18:00:00 +0000 + +ist (1.2.3-1) stable; urgency=medium + + * Fixed redo bug (redo was calling undo) + + * Segmentation context does not create an Image unless asked to now. This + prevents it initializing the swt Display object unless it needs to, allowing + console applications that use the core segmentation stuff to be created + without initializing the Display. + + -- Kevin McGuinness Thu, 8 Jan 2009 15:00:00 +0000 + +ist (1.2.2-1) stable; urgency=low + + * Fixed application name in menu bar on OS X + + -- Kevin McGuinness Tue, 6 Jan 2009 15:00:00 +0000 + +ist (1.2.1-1) stable; urgency=low + + * Use command key instead of ctrl key for shortcuts on Mac OSX + + * Zip file for app bundle on OS X: it's better supported than tar.gz + + * Modified about box to display SWT version + + * Added standard Command+, binding for preferences on Mac + + * Use small icons in menus on all platforms + + * Removed icons in preference dialog buttons on Mac OS + + * Improved and included several new icons + + * Fixed visual bug on linux: horizontal separators didn't look right in + the export dialog. + + -- Kevin McGuinness Wed, 17 Dec 2008 17:00:00 +0000 + +ist (1.2.0-1) stable; urgency=low + + * New feature: export to HTML image maps integrated. Supports exporting + polygons and rectangles for now. Can export several rollover effects using + the javascript image swapping technique. + + * Fixed bug on Mac OS X: application did not always exit properly + + * Open browser window to show the HTML page when export completes + + * Option to turn on or off the open browser window behavior + + * Best practices: use var for global image preloads. + + -- Kevin McGuinness Tue, 16 Dec 2008 21:37:00 +0000 + +ist (1.1.0-1) stable; urgency=low + + * Several new features are planned for introduction, so the version number + has been incremented to 1.1 + + * Changed context file format. This is now stored as a zip archive including + the image, mask and annotations, so that it is more "self-contained". The + new file format is NOT compatible with the old one. + + -- Kevin McGuinness Tue, 29 Feb 2008 12:25:00 +0000 + +ist (1.0.6-1) stable; urgency=low + + * Added startup check for gdiplus.dll for platforms where it's needed but not + installed by default. Program will now show a useful message and exit gracefully + instead of spewing stack traces on the console and behaving oddly. + + * Fixed bug that caused the auto-apply toggle button to be always disabled + after first image load. + + -- Kevin McGuinness Tue, 28 Feb 2008 10:36:00 +0000 + +ist (1.0.5-1) stable; urgency=medium + + * Fixed bug that caused the brush control (and some other toolbar buttons) to be + disabled after an experiment was cancelled. + + -- Kevin McGuinness Tue, 12 Feb 2008 13:25:00 +0000 + +ist (1.0.4-1) stable; urgency=low + + * Updated the integrated help file (help.html) + + -- Kevin McGuinness Tue, 28 Jan 2008 16:40:00 +0000 + +ist (1.0.3-1) stable; urgency=low + + * MacOSX PowerPC now supported + + * Mostly non-functional changes on Linux, refactoring and the like + + -- Kevin McGuinness Tue, 25 Jan 2008 16:08:00 +0000 + +ist (1.0.2-1) stable; urgency=low + + * Application icon added + + -- Kevin McGuinness Tue, 16 Jan 2008 19:43:00 +0000 + +ist (1.0.1-1) stable; urgency=low + + * Created debian package + + * Support added for 64 bit linux + + -- Kevin McGuinness Tue, 15 Jan 2008 19:43:00 +0000 diff --git a/image_annotation/resources/plaf/debian/copyright b/image_annotation/resources/plaf/debian/copyright new file mode 100644 index 0000000..32da294 --- /dev/null +++ b/image_annotation/resources/plaf/debian/copyright @@ -0,0 +1,10 @@ +ist + +Copyright: Kevin McGuinness + Center for Digital Video Processing + Dublin City University + +2008-01-14 + +The home page of ist is at: +https://www.kspace.cdvp.dcu.ie/public/interactive-segmentation diff --git a/image_annotation/resources/plaf/debian/desktop b/image_annotation/resources/plaf/debian/desktop new file mode 100644 index 0000000..718ced1 --- /dev/null +++ b/image_annotation/resources/plaf/debian/desktop @@ -0,0 +1,10 @@ +# freedesktop.org desktop entry +[Desktop Entry] +Version=1.0 +Type=Application +Name=Interactive Segmentation Tool +Comment=Interactively extract objects from images using various algorithms +Icon=ist +Exec=ist +Terminal=false +Categories=GTK;Graphics diff --git a/image_annotation/resources/plaf/debian/ist b/image_annotation/resources/plaf/debian/ist new file mode 100644 index 0000000..d1e385d --- /dev/null +++ b/image_annotation/resources/plaf/debian/ist @@ -0,0 +1,36 @@ +#!/bin/sh + +# Setup +data=@data@ +main=@main@ +jars=@jars@ + +# Change directory +cd "${data}" + +# This is needed for the browser widget on linux +if [ -z "${MOZILLA_FIVE_HOME}" ]; then + # Check if common locations exist + if [ -d "/usr/lib/firefox" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/firefox + elif [ -d "/usr/lib/mozilla-firefox" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/mozilla-firefox + elif [ -d "/usr/lib/mozilla" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/mozilla + fi +fi + +# Set library path +if [ -n "${MOZILLA_FIVE_HOME}" ]; then + export LD_LIBRARY_PATH=${MOZILLA_FIVE_HOME}:${LD_LIBRARY_PATH} +fi + +# Launch +if [ -n "${JAVA_HOME}" ]; then + ${JAVA_HOME}/bin/java -classpath "${jars}" ${main} $* +else + java -classpath "${jars}" ${main} $* +fi + +cd "$OLDPWD" + diff --git a/image_annotation/resources/plaf/debian/ist.1 b/image_annotation/resources/plaf/debian/ist.1 new file mode 100644 index 0000000..29faaf6 --- /dev/null +++ b/image_annotation/resources/plaf/debian/ist.1 @@ -0,0 +1,29 @@ +.TH "ist" "1" "1.3.3" "Kevin McGuinness" "Graphics" +.SH "NAME" +.LP +ist \- Interactive Segmentation Tool +.SH "SYNTAX" +.LP +ist [options] + +.SH "DESCRIPTION" +.LP +Launches the interactive segmentation tool graphical user interface + +.SH "ENVIRONMENT VARIABLES" +.LP +.TP +\fBMOZILLA_FIVE_HOME\fP +Specifies the location of the mozilla 5 browser +(firefox etc.). This is usually something like +/usr/lib/firefox + +.SH "EXAMPLES" +.LP +To run this program the standard way type: +.LP +$ ist + +.SH "AUTHORS" +.LP +Kevin McGuinness diff --git a/image_annotation/resources/plaf/debian/postinst b/image_annotation/resources/plaf/debian/postinst new file mode 100644 index 0000000..fb29bea --- /dev/null +++ b/image_annotation/resources/plaf/debian/postinst @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo update-desktop-database +sudo gtk-update-icon-cache /usr/share/icons/hicolor diff --git a/image_annotation/resources/plaf/debian/postrm b/image_annotation/resources/plaf/debian/postrm new file mode 100644 index 0000000..fb29bea --- /dev/null +++ b/image_annotation/resources/plaf/debian/postrm @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo update-desktop-database +sudo gtk-update-icon-cache /usr/share/icons/hicolor diff --git a/image_annotation/resources/plaf/linux/ist b/image_annotation/resources/plaf/linux/ist new file mode 100644 index 0000000..1f45a3a --- /dev/null +++ b/image_annotation/resources/plaf/linux/ist @@ -0,0 +1,34 @@ +#!/bin/sh + +jars=istapp.jar:istapi.jar:swt.jar:jface.jar +main=ie.dcu.apps.ist.Main + +# Change directory +cd "`dirname "$0"`" + +# This is needed for the SWT browser widget on linux +if [ -z "${MOZILLA_FIVE_HOME}" ]; then + + # Check if common locations exist + if [ -d "/usr/lib/firefox" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/firefox + elif [ -d "/usr/lib/mozilla-firefox" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/mozilla-firefox + elif [ -d "/usr/lib/mozilla" ]; then + export MOZILLA_FIVE_HOME=/usr/lib/mozilla + fi + +fi + +# Set library path +if [ -n "${MOZILLA_FIVE_HOME}" ]; then + export LD_LIBRARY_PATH=${MOZILLA_FIVE_HOME}:${LD_LIBRARY_PATH} +fi + +if [ -n "${JAVA_HOME}" ]; then + ${JAVA_HOME}/bin/java -classpath "${jars}" ${main} $* +else + java -classpath "${jars}" ${main} $* +fi + +cd "$OLDPWD" diff --git a/image_annotation/resources/plaf/mac/Info.plist b/image_annotation/resources/plaf/mac/Info.plist new file mode 100644 index 0000000..0fbcac6 --- /dev/null +++ b/image_annotation/resources/plaf/mac/Info.plist @@ -0,0 +1,27 @@ + + + + + CFBundleName + @name@ + CFBundleShortVersionString + @version@ + CFBundleGetInfoString + @synopsis@ (v@version@) + CFBundleAllowMixedLocalizations + false + CFBundleInfoDictionaryVersion + 6.0 + CFBundleExecutable + @exename@ + CFBundleDevelopmentRegion + English + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleIconFile + @icons@ + + diff --git a/image_annotation/resources/plaf/mac/PkgInfo b/image_annotation/resources/plaf/mac/PkgInfo new file mode 100644 index 0000000..6f749b0 --- /dev/null +++ b/image_annotation/resources/plaf/mac/PkgInfo @@ -0,0 +1 @@ +APPL???? diff --git a/image_annotation/resources/plaf/mac/icon.icns b/image_annotation/resources/plaf/mac/icon.icns new file mode 100644 index 0000000..ae867d3 Binary files /dev/null and b/image_annotation/resources/plaf/mac/icon.icns differ diff --git a/image_annotation/resources/plaf/mac/ist b/image_annotation/resources/plaf/mac/ist new file mode 100644 index 0000000..07663d1 --- /dev/null +++ b/image_annotation/resources/plaf/mac/ist @@ -0,0 +1,34 @@ +#!/bin/sh + +# What kinda mac are we on +CPU=`uname -p` + +# Absolute path to where the app was launched from +APP_PACKAGE="`dirname "$0"`/../.." +APP_PACKAGE="`cd "${APP_PACKAGE}"; pwd`" + +# Locations of resources, jars, and libs +LIB_DIR="Libraries/${CPU}" + +# Move to the resource directory +cd "${APP_PACKAGE}/Contents/Resources" + +# Establish whether we should use the 32 bit or 64 bit cocoa library +SWT_LIBNAME=swt-cocoa-32.jar +java -version 2>&1 | grep "64-Bit" > /dev/null +if [ "$?" -eq "0" ]; then + SWT_LIBNAME=swt-cocoa-64.jar +fi + +echo "SWT Library: ${SWT_LIBNAME}" + +# Launch app +java \ + -XstartOnFirstThread \ + -classpath "Java/istapp.jar:Java/istapi.jar:Java/jface.jar:Java/${SWT_LIBNAME}" \ + -Dorg.eclipse.swt.internal.carbon.noFocusRing \ + -Dorg.eclipse.swt.internal.carbon.smallFonts \ + @appmain@ $* + +# Go back to the old working directory +cd "${OLDPWD}" diff --git a/image_annotation/resources/plaf/windows/application.ico b/image_annotation/resources/plaf/windows/application.ico new file mode 100644 index 0000000..ae1ac33 Binary files /dev/null and b/image_annotation/resources/plaf/windows/application.ico differ diff --git a/image_annotation/resources/plaf/windows/install.xml b/image_annotation/resources/plaf/windows/install.xml new file mode 100644 index 0000000..23cb85e --- /dev/null +++ b/image_annotation/resources/plaf/windows/install.xml @@ -0,0 +1,56 @@ + + + + + + + + + @name@ + @version@ + + + + @website@ + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Base installation files + + + + + + + diff --git a/image_annotation/resources/plaf/windows/installer.ico b/image_annotation/resources/plaf/windows/installer.ico new file mode 100644 index 0000000..66d530f Binary files /dev/null and b/image_annotation/resources/plaf/windows/installer.ico differ diff --git a/image_annotation/resources/plaf/windows/installer.jsmooth b/image_annotation/resources/plaf/windows/installer.jsmooth new file mode 100644 index 0000000..823d035 --- /dev/null +++ b/image_annotation/resources/plaf/windows/installer.jsmooth @@ -0,0 +1,29 @@ + + + registry + javahome + jrepath + jdkpath + exepath + jview + @input@ + @icon@ + ${EXECUTABLEPATH} + true + @output@ + -1 + com.izforge.izpack.installer.Installer + -1 + + 1.5 + Windowed Wrapper + + Message + Java has not been found on your computer. + Do you want to download it? + + + Debug + 0 + + diff --git a/image_annotation/resources/plaf/windows/ist.jsmooth b/image_annotation/resources/plaf/windows/ist.jsmooth new file mode 100644 index 0000000..52388f9 --- /dev/null +++ b/image_annotation/resources/plaf/windows/ist.jsmooth @@ -0,0 +1,31 @@ + + + registry + javahome + jrepath + jdkpath + exepath + jview + istapp.jar + istapi.jar + swt-win.jar + jface.jar + ${EXECUTABLEPATH} + false + ist.exe + application.ico + -1 + ie.dcu.apps.ist.Main + -1 + 1.5.0 + Windowed Wrapper + + Message + This program needs Java to run. + Please download it at http://www.java.com + + + Debug + 0 + + diff --git a/image_annotation/resources/plaf/windows/shortcuts.xml b/image_annotation/resources/plaf/windows/shortcuts.xml new file mode 100644 index 0000000..d34c867 --- /dev/null +++ b/image_annotation/resources/plaf/windows/shortcuts.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + diff --git a/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/description.skel b/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/description.skel new file mode 100644 index 0000000..52acb08 --- /dev/null +++ b/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/description.skel @@ -0,0 +1,52 @@ + + +false +SKEL_SIMPLEWRAPPER_DESCRIPTION +jwrap.exe +JAVA +102 +103 +Windowed Wrapper + +SKEL_SIMPLEWRAPPER_PROPERTY_MESSAGE_DESCRIPTION +Message + +textarea +Java has not been found on your computer. Do you want to download it? + + +SKEL_SIMPLEWRAPPER_PROPERTY_URL_DESCRIPTION +URL + +string +http://www.java.com + + +SKEL_GENERIC_PROPERTY_SINGLEPROCESS_DESCRIPTION +SingleProcess + +boolean +0 + + +SKEL_GENERIC_SINGLEINSTANCE_DESCRIPTION +SingleInstance + +boolean +0 + + +SKEL_GENERIC_JNISMOOTH_DESCRIPTION +JniSmooth + +boolean +0 + + +SKEL_GENERIC_PROPERTY_DEBUG_DESCRIPTION +Debug + +boolean +0 + + diff --git a/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/jwrap.exe b/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/jwrap.exe new file mode 100755 index 0000000..436402d Binary files /dev/null and b/image_annotation/resources/plaf/windows/skeletons/windowed-wrapper/jwrap.exe differ diff --git a/image_annotation/src/ie/dcu/apps/ist/AppPrefs.java b/image_annotation/src/ie/dcu/apps/ist/AppPrefs.java new file mode 100644 index 0000000..fabc554 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppPrefs.java @@ -0,0 +1,259 @@ +package ie.dcu.apps.ist; + +import ie.dcu.swt.SwtUtils; + +import java.util.*; +import java.util.logging.Logger; +import java.util.prefs.*; + +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; + +/** + * Manages application preferences + * + * @author Kevin McGuinness + */ +public class AppPrefs { + + public interface Keys { + public static final String ENABLE_FEEDBACK = "enable-feedback"; + public static final String FOREGROUND_COLOR = "foreground-color"; + public static final String BACKGROUND_COLOR = "background-color"; + public static final String EXPERIMENT_EMBEDDED = "experiment-embedded"; + public static final String CONFIRM_EXIT = "confirm-exit"; + } + + public interface SupportedTypes { + public static final Class INTEGER = Integer.class; + public static final Class DOUBLE = Double.class; + public static final Class FLOAT = Float.class; + public static final Class SHORT = Short.class; + public static final Class BYTE = Byte.class; + public static final Class LONG = Long.class; + public static final Class STRING = String.class; + public static final Class BOOLEAN = Boolean.class; + public static final Class RGB = RGB.class; + } + + + private final Logger log; + private final Preferences prefs; + private final List listeners; + + + public AppPrefs() { + this.log = Logger.getLogger(getClass().getName()); + this.prefs = Preferences.userRoot().node(Application.APP_ID); + this.listeners = new LinkedList(); + + addSwtThreadAdaptionListener(); + } + + + private void addSwtThreadAdaptionListener() { + prefs.addPreferenceChangeListener(new PreferenceChangeListener() { + public void preferenceChange(PreferenceChangeEvent evt) { + firePreferenceChangedSynced(evt); + } + }); + } + + + private void firePreferenceChangedSynced(final PreferenceChangeEvent evt) { + + // Delegate to event SWT event dispatching thread + Display.getDefault().syncExec(new Runnable() { + public void run() { + firePreferenceChanged(evt); + } + }); + + } + + + private void firePreferenceChanged(PreferenceChangeEvent evt) { + for (PreferenceChangeListener listener : listeners) { + listener.preferenceChange(evt); + } + } + + + public void sync() { + try { + prefs.sync(); + } catch (BackingStoreException e) { + log.warning("Error syncing preferences: " + e.getMessage()); + } + } + + + public void flush() { + try { + prefs.flush(); + } catch (BackingStoreException e) { + log.warning("Error writing preferences: " + e.getMessage()); + } + } + + + public void clear() { + try { + prefs.clear(); + } catch (BackingStoreException e) { + log.warning("Error clearing preferences: " + e.getMessage()); + } + } + + + public String[] keys() { + try { + return prefs.keys(); + } catch (BackingStoreException e) { + log.warning("Error retrieving preference keys: " + e.getMessage()); + } + return new String[0]; + } + + + public void remove(String key) { + prefs.remove(key); + } + + + public T get(Class clazz, String key, T def) { + String value = (def != null) ? + get(key, def.toString()) : get(key, null); + + return decode(clazz, value, def); + } + + + public void put(Class clazz, String key, T value) { + prefs.put(key, encode(clazz, value)); + } + + + public void put(String key, String value) { + prefs.put(key, value); + } + + + public String get(String key, String def) { + return prefs.get(key, def); + } + + + public void addPreferenceChangeListener(PreferenceChangeListener pcl) { + listeners.add(pcl); + } + + + public void removePreferenceChangeListener(PreferenceChangeListener pcl) { + listeners.remove(pcl); + } + + + public String encode(Class clazz, T value) { + if (clazz.equals(RGB.class)) { + return encodeRGB((RGB) value); + } else if (value != null) { + return String.valueOf(value); + } else { + return null; + } + } + + + public T decode(Class clazz, String str, T def) { + if (str == null) { + return def; + } else if (isNumeric(clazz)) { + return clazz.cast(decodeNumeric(clazz, str, def)); + } else if (isString(clazz)) { + return clazz.cast(str); + } else if (isBoolean(clazz)) { + return clazz.cast(decodeBoolean(str, (Boolean) def)); + } else if (clazz.equals(RGB.class)) { + return clazz.cast(decodeRGB(str, (RGB) def)); + } else { + log.warning("Unable to decode class: " + clazz.getName()); + } + + return def; + } + + + private boolean isString(Class clazz) { + return clazz.equals(String.class); + } + + + private boolean isBoolean(Class clazz) { + return clazz.equals(Boolean.class); + } + + + + private boolean isNumeric(Class clazz) { + return + clazz.equals(Integer.class) || + clazz.equals(Double.class) || + clazz.equals(Float.class) || + clazz.equals(Short.class) || + clazz.equals(Byte.class) || + clazz.equals(Long.class); + } + + + private RGB decodeRGB(String str, RGB def) { + if (str.startsWith("#")) { + try { + int hex = Integer.parseInt(str.substring(1), 16); + return SwtUtils.int2rgb(hex); + } catch (NumberFormatException e) { + log.warning("Not a valid color: " + str); + } + } + return def; + } + + + private String encodeRGB(RGB rgb) { + return String.format("#%06x", SwtUtils.rgb2int(rgb)); + } + + + private boolean decodeBoolean(String str, boolean def) { + return str.equalsIgnoreCase("true") || + (str.equalsIgnoreCase("false") ? false : def); + } + + + private T decodeNumeric(Class clazz, String s, T def) { + try { + if (clazz.equals(Integer.class)) { + return clazz.cast(Integer.parseInt(s)); + + } else if (clazz.equals(Double.class)) { + return clazz.cast(Double.parseDouble(s)); + + } else if (clazz.equals(Float.class)) { + return clazz.cast(Float.parseFloat(s)); + + } else if (clazz.equals(Short.class)) { + return clazz.cast(Short.parseShort(s)); + + } else if (clazz.equals(Byte.class)) { + return clazz.cast(Byte.parseByte(s)); + + } else if (clazz.equals(Long.class)) { + return clazz.cast(Long.parseLong(s)); + + } + } catch (NumberFormatException ex) { + log.warning("Invalid numeric value in preferences: " + s); + } + return def; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/AppPrefsManager.java b/image_annotation/src/ie/dcu/apps/ist/AppPrefsManager.java new file mode 100644 index 0000000..e41ea3b --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppPrefsManager.java @@ -0,0 +1,97 @@ +package ie.dcu.apps.ist; + +import static ie.dcu.segment.annotate.AnnotationType.*; +import static ie.dcu.apps.ist.AppPrefs.Keys.*; +import static ie.dcu.apps.ist.AppPrefs.SupportedTypes.*; +import ie.dcu.segment.annotate.*; +import ie.dcu.apps.ist.AppPrefs.*; + +import java.util.prefs.*; + +import org.eclipse.jface.resource.*; +import org.eclipse.swt.graphics.*; + +/** + * Manages updating various components when preferences change. + * + * @author Kevin McGuinness + */ +class AppPrefsManager implements PreferenceChangeListener { + + private final AppWindow window; + private final ColorRegistry colorRegistry; + private final AppPrefs prefs; + + + public AppPrefsManager(AppWindow window) { + this.window = window; + this.colorRegistry = JFaceResources.getColorRegistry(); + this.prefs = loadPreferences(); + } + + + private AppPrefs loadPreferences() { + AppPrefs prefs = new AppPrefs(); + prefs.addPreferenceChangeListener(this); + return prefs; + } + + + public AppPrefs getPrefs() { + return prefs; + } + + + public void update() { + updateFeedbackEnabled(); + updateBackgroundColor(); + updateForegroundColor(); + updateExperimentEmbedded(); + } + + + public void updateExperimentEmbedded() { + boolean on = prefs.get(BOOLEAN, Keys.EXPERIMENT_EMBEDDED, false); + window.setExperimentModeEmbedded(on); + } + + + public void updateFeedbackEnabled() { + boolean on = prefs.get(BOOLEAN, Keys.ENABLE_FEEDBACK, true); + window.setEnableFeedback(on); + } + + + public void updateForegroundColor() { + RGB color = prefs.get(RGB, Keys.FOREGROUND_COLOR, DEFAULT_FOREGROUND); + colorRegistry.put(AnnotationType.Foreground.key, color); + window.getView().repaint(); + } + + + public void updateBackgroundColor() { + RGB color = prefs.get(RGB, Keys.BACKGROUND_COLOR, DEFAULT_BACKGROUND); + colorRegistry.put(AnnotationType.Background.key, color); + window.getView().repaint(); + } + + + public void preferenceChange(PreferenceChangeEvent evt) { + String pref = evt.getKey(); + + if (pref.equals(ENABLE_FEEDBACK)) { + updateFeedbackEnabled(); + + } else if (pref.equals(FOREGROUND_COLOR)) { + updateForegroundColor(); + + } else if (pref.equals(BACKGROUND_COLOR)) { + updateBackgroundColor(); + + } else if (pref.equals(EXPERIMENT_EMBEDDED)) { + updateExperimentEmbedded(); + } + + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/AppRecentFiles.java b/image_annotation/src/ie/dcu/apps/ist/AppRecentFiles.java new file mode 100644 index 0000000..c2b7b31 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppRecentFiles.java @@ -0,0 +1,93 @@ +package ie.dcu.apps.ist; + +import ie.dcu.apps.ist.recent.*; + +import java.io.*; + +/** + * Manages applications recent files list. + * + * @author Kevin McGuinness + */ +public class AppRecentFiles extends RecentFiles { + public static final String APP_SETTINGS_PATH = ".ist"; + public static final String HISTORY_FILE_NAME = ".history"; + + private static AppRecentFiles history; + + + private AppRecentFiles(File store) { + super(store); + } + + + public static AppRecentFiles getInstance() { + if (history == null) { + history = create(); + } + return history; + } + + + private static AppRecentFiles create() { + AppRecentFiles history = null; + try { + history = new AppRecentFiles(getHistoryFile()); + history.load(); + } catch (IOException e) { + getLogger().warning("Unable to create history file: " + e); + } + + return history; + } + + + @Override + public void load() { + try { + super.load(); + } catch (IOException e) { + getLogger().warning("Error loading recent documents list: " + e); + } + } + + @Override + public void store() { + try { + super.store(); + } catch (IOException e) { + getLogger().warning("Error storing recent documents list: " + e); + } + } + + + private static File getHistoryFile() throws IOException { + File file = new File(getSettingsDirectory(), HISTORY_FILE_NAME); + if (file.exists()) { + // Check we can write + if (file.canWrite()) { + return file; + } + } else { + return file; + } + + throw new IOException("History file is unwritable"); + } + + + private static File getSettingsDirectory() throws IOException { + String home = System.getProperty("user.home"); + File settings = new File(home, APP_SETTINGS_PATH); + + if (!settings.exists()) { + if (settings.mkdirs()) { + return settings; + } + } else { + return settings; + } + + throw new IOException("Unable to create application settings directory"); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/AppRecentMenu.java b/image_annotation/src/ie/dcu/apps/ist/AppRecentMenu.java new file mode 100644 index 0000000..84fe0cb --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppRecentMenu.java @@ -0,0 +1,113 @@ +/** + * + */ +package ie.dcu.apps.ist; + +import ie.dcu.apps.ist.actions.ActionManager; +import ie.dcu.apps.ist.actions.HoverMenuManager; +import ie.dcu.apps.ist.actions.OpenAction; +import ie.dcu.apps.ist.actions.OpenRecentAction; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Properties; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; + +/** + * Recent files menu. + * + * @author Kevin McGuinness + */ +class AppRecentMenu extends HoverMenuManager { + private final AppRecentFiles history; + private final EmptyAction empty; + private final AppWindow window; + private IAction action; + + public AppRecentMenu(AppWindow window, IMenuManager parent) { + super("Open &Recent"); + + this.window = window; + setParent(parent); + history = AppRecentFiles.getInstance(); + add(empty = new EmptyAction()); + addMenuListener(menuListener); + + ActionManager actions = window.getActions(); + if (actions != null) { + OpenAction openAction = actions.get(OpenAction.class); + openAction.addPropertyChangeListener(actionListener); + action = openAction; + } + + try { + Properties props = window.getProperties(); + String icon = props.getProperty("AppRecentMenu.icon", + "file:icons/open-recent.png"); + setImageURL(new URL(icon)); + } catch (MalformedURLException e) { + // Ignore + } + } + + @Override + public void fill(Menu parent, int index) { + super.fill(parent, index); + MenuItem item = getMenuItem(); + if (item != null) { + item.setEnabled(action.isEnabled()); + } + } + + + private void buildMenu() { + removeAll(); + + if (history.empty()) { + add(empty); + } else { + for (File file : history) { + add(new OpenRecentAction(window.getActions(), file)); + } + } + + update(false); + } + + + private final class EmptyAction extends Action { + public EmptyAction() { + setText("No Recent Files"); + setEnabled(false); + } + }; + + + private final IMenuListener menuListener = new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + buildMenu(); + } + }; + + + private final IPropertyChangeListener actionListener + = new IPropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent event) { + MenuItem item = getMenuItem(); + if (item != null) { + item.setEnabled(action.isEnabled()); + } + } + }; + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/AppStatus.java b/image_annotation/src/ie/dcu/apps/ist/AppStatus.java new file mode 100644 index 0000000..b888e21 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppStatus.java @@ -0,0 +1,46 @@ +package ie.dcu.apps.ist; + +import java.net.*; + +import org.eclipse.jface.resource.*; +import org.eclipse.swt.graphics.*; + +/** + * Application status bar message type. + * + * @author Kevin McGuinness + */ +public enum AppStatus { + Information, Warning, Error; + + public Image getIcon() { + String key = key(); + Image image = JFaceResources.getImage(key); + if (image == null) { + ImageRegistry registry = JFaceResources.getImageRegistry(); + ImageDescriptor descriptor = createImageDescriptor(); + registry.put(key, descriptor); + image = registry.get(key); + } + return image; + } + + private ImageDescriptor createImageDescriptor() { + try { + return ImageDescriptor.createFromURL(new URL(key())); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + private String key() { + switch (this) { + case Information: + return "file:resources/icons/dialog-information.png"; + case Warning: + return "file:resources/icons/dialog-warning.png"; + default: + return "file:resources/icons/dialog-error.png"; + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/AppWindow.java b/image_annotation/src/ie/dcu/apps/ist/AppWindow.java new file mode 100644 index 0000000..a047dbe --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/AppWindow.java @@ -0,0 +1,725 @@ +package ie.dcu.apps.ist; + +import ie.dcu.apps.ist.actions.*; +import ie.dcu.apps.ist.event.*; +import ie.dcu.apps.ist.exp.Experiment; +import ie.dcu.apps.ist.views.*; +import ie.dcu.segment.*; +import ie.dcu.segment.annotate.*; +import ie.dcu.swt.*; +import ie.dcu.swt.event.*; +import ie.dcu.swt.layout.LayoutFactory; +import ie.dcu.util.*; + +import java.io.*; +import java.net.*; +import java.util.Properties; +import java.util.logging.*; + +import org.eclipse.jface.action.*; +import org.eclipse.jface.resource.*; +import org.eclipse.jface.window.ApplicationWindow; +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.*; + +/** + * Main interactive segmentation tool application window. + * + * @author Kevin McGuinness + */ +public class AppWindow extends ApplicationWindow implements FileDropListener { + private static final Logger log = Logger.getLogger("AppWindow"); + + private final Properties props; + + private AppPrefsManager prefsManager; + private ActionManager actions; + private Composite content; + private SegmentationView view; + private Shell shell; + private ImageObserver observer; + private Experiment experiment; + private ExperimentPanel experimentPanel; + private boolean experimentModeEmbedded; + + + public AppWindow() { + super(null); + + props = loadProperties(); + + addMenuBar(); + addStatusLine(); + } + + + @Override + protected boolean canHandleShellCloseEvent() { + if (!ExitAction.confirmExit(this)) { + return false; + } + return super.canHandleShellCloseEvent(); + } + + + private Properties loadProperties() { + try { + return Application.loadProperties(getPropertiesFile()); + } catch (IOException e) { + log.severe("Unable to load properties file " + e.getMessage()); + throw new RuntimeException(e); + } + } + + private String getPropertiesFile() { + return OsUtils.isMacOS() ? "application.mac" : "application"; + } + + + public void updateWindowTitle() { + String title; + if (hasContext()) { + String file = getContext().getFile().getName(); + title = String.format("%s [%s]", Application.APP_NAME, file); + + } else { + title = Application.APP_NAME; + } + + if (isExperimentMode()) { + title += " - Experiment Mode"; + } + + getShell().setText(title); + } + + + private SegmentationView createView(Composite parent) { + + // Load props + Properties props; + try { + props = Application.loadProperties("view"); + } catch (IOException e) { + log.severe("Error loading view properties " + e.getLocalizedMessage()); + throw new RuntimeException(e); + } + + // Create view + SegmentationView view = new SegmentationView(props, parent, 0); + + boolean blocksOnFork = false; + + // Set runnable context + view.setRunnableContext(this, blocksOnFork); + + // Add observer + observer = new ImageObserver(view); + + // Return new view + return view; + } + + + private static ImageDescriptor createImageDescriptor(String url) { + try { + return ImageDescriptor.createFromURL(new URL(url)); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + + private void updateEnabledActions() { + ActionManager a = getActions(); + boolean ctxAvailable = hasContext(); + boolean experimentMode = isExperimentMode(); + + a.setEnabled(OpenAction.class, !experimentMode); + a.setEnabled(PreferencesAction.class, !experimentMode); + a.setEnabled(SaveAction.class, ctxAvailable && !experimentMode); + a.setEnabled(SaveAsAction.class, ctxAvailable && !experimentMode); + a.setEnabled(ExportViewAction.class, ctxAvailable && !experimentMode); + a.setEnabled(ExportTransparentPNGAction.class, ctxAvailable && !experimentMode); + a.setEnabled(PrintAction.class, ctxAvailable && !experimentMode); + a.setEnabled(CopyAction.class, ctxAvailable && !experimentMode); + a.setEnabled(UndoAction.class, ctxAvailable && view.canUndo()); + a.setEnabled(RedoAction.class, ctxAvailable && view.canRedo()); + a.setEnabled(NextAction.class, ctxAvailable && !experimentMode); + a.setEnabled(PreviousAction.class, ctxAvailable && !experimentMode); + a.setEnabled(OpenExperimentAction.class, !experimentMode); + + updateExportImageMapAction(); + + if (experimentMode) { + + boolean changeSelection = false; + + // Enable experiment segmenters + for (SelectSegmenterAction s : a.getSegmentationActions()) { + Segmenter segmenter = s.getSegmenter(); + boolean enabled = experiment.using(segmenter); + s.setEnabled(enabled); + + if (!s.isEnabled() && s.isChecked()) { + // The selected segmenter has been disabled, + // we need to select a different one + changeSelection = true; + s.setChecked(false); + } + } + + if (changeSelection) { + // Select the first available segmenter + for (SelectSegmenterAction s : a.getSegmentationActions()) { + if (experiment.using(s.getSegmenter())) { + s.setChecked(true); + s.run(); + break; + } + } + } + + } else { + // Enable all select segmenter actions + for (SelectSegmenterAction s : a.getSegmentationActions()) { + s.setEnabled(true); + } + } + } + + private void updateExportImageMapAction() { + ActionManager a = getActions(); + SegmentationContext ctx = getContext(); + + if (ctx != null && !isExperimentMode()) { + AnnotationManager am = ctx.getAnnotations(); + boolean hasObject = am.hasForegroundAnnotation() + && am.hasBackgroundAnnotation(); + a.setEnabled(ExportImageMapAction.class, hasObject); + + } else { + a.setEnabled(ExportImageMapAction.class, false); + } + } + + + private void updateUndoRedoActions() { + ActionManager a = getActions(); + boolean ctxAvailable = hasContext(); + a.setEnabled(UndoAction.class, ctxAvailable && view.canUndo()); + a.setEnabled(RedoAction.class, ctxAvailable && view.canRedo()); + } + + + @Override + protected void configureShell(Shell shell) { + this.shell = shell; + super.configureShell(shell); + updateWindowTitle(); + loadIcons(shell); + shell.setSize(800, 600); + SwtUtils.center(shell); + } + + + private void loadIcons(Shell shell) { + final String[] icons = { + "resources/icons/icon-16.png", + "resources/icons/icon-24.png", + "resources/icons/icon-32.png", + "resources/icons/icon-48.png" + }; + + Image[] images = new Image[icons.length]; + Display display = shell.getDisplay(); + + try { + int i = 0; + for (String file : icons) { + images[i++] = new Image(display, file); + } + + shell.setImages(images); + + } catch (SWTException e) { + log.warning("Error loading application window icons: " + + e.getLocalizedMessage()); + } + } + + + @Override + protected Control createContents(Composite parent) { + content = new Composite(parent, SWT.NONE); + + content.setLayout(LayoutFactory.createGridLayout(0, 0, 2, false)); + + view = createView(content); + view.setLayoutData(LayoutFactory.createGridData()); + view.addContextChangeListener(contextListener); + + SwtUtils.createFileDropTarget(view, this); + + // Done creating contents + getPrefsManager().update(); + updateEnabledActions(); + + return content; + } + + + @Override + protected boolean showTopSeperator() { + return false; + } + + + @Override + protected MenuManager createMenuManager() { + MenuManager bar = new MenuManager(); + + ActionManager actions = getActions(); + + MenuManager file = addMenu(bar, "&File"); + file.add(actions.get(OpenAction.class)); + + file.add(new AppRecentMenu(this, file)); + + file.add(new Separator()); + file.add(actions.get(SaveAction.class)); + file.add(actions.get(SaveAsAction.class)); + file.add(new Separator()); + + // Export menu -> + MenuManager exportMenu = addMenu(file, "&Export", + props.getProperty("ExportMenu.icon")); + + exportMenu.add(actions.get(ExportViewAction.class)); + exportMenu.add(actions.get(ExportImageMapAction.class)); + exportMenu.add(actions.get(ExportTransparentPNGAction.class)); + + file.add(new Separator()); + file.add(actions.get(PrintAction.class)); + file.add(new Separator()); + file.add(actions.get(ExitAction.class)); + + MenuManager edit = addMenu(bar, "&Edit"); + + edit.add(actions.get(UndoAction.class)); + edit.add(actions.get(RedoAction.class)); + edit.add(new Separator()); + edit.add(actions.get(CopyAction.class)); + edit.add(new Separator()); + edit.add(actions.get(PreferencesAction.class)); + + MenuManager tools = addMenu(bar, "&Tools"); + tools.add(actions.get(OpenExperimentAction.class)); + tools.add(new Separator()); + + for (SelectSegmenterAction a : actions.getSegmentationActions()) { + tools.add(a); + } + + tools.add(new Separator()); + tools.add(actions.get(ConfigureSegmenterAction.class)); + + MenuManager go = addMenu(bar, "&Go"); + go.add(actions.get(NextAction.class)); + go.add(actions.get(PreviousAction.class)); + + + MenuManager help = addMenu(bar, "&Help"); + + help.add(actions.get(HelpAction.class)); + help.add(actions.get(AboutAction.class)); + + if (OsUtils.isMacOSX()) { + // Enhance the UI on the Mac + IAction aboutAction = actions.get(AboutAction.class); + IAction prefAction = actions.get(PreferencesAction.class); + Listener quitListener = new Listener() { + public void handleEvent(Event evt) { + getActions().get(ExitAction.class).runWithEvent(evt); + } + }; + CocoaUIEnhancer enhancer = new CocoaUIEnhancer(Application.APP_NAME); + enhancer.hookApplicationMenu(Display.getDefault(), + quitListener, aboutAction, prefAction); + } + + return bar; + } + + + protected static MenuManager addMenu(MenuManager parent, String text) { + MenuManager menu = new HoverMenuManager(text); + parent.add(menu); + return menu; + } + + protected static MenuManager addMenu(MenuManager parent, + String text, String image) { + + MenuManager menu; + try { + menu = new HoverMenuManager(text, image != null ? + new URL(image) : null); + } catch (MalformedURLException e) { + log.log(Level.WARNING, "Malformed URL", e); + menu = new HoverMenuManager(text); + } + + parent.add(menu); + return menu; + } + + + + public void setExperiment(Experiment ex) { + if (experiment != ex) { + experiment = ex; + setContext(null); + + if (ex == null) { + + if (experimentPanel != null) { + experimentPanel.dispose(); + experimentPanel = null; + content.layout(); + } + } else { + + if (experimentModeEmbedded) { + experimentPanel = new ExperimentPanel(this, content, SWT.NONE); + GridData data = new GridData(SWT.FILL, SWT.FILL, false, true); + data.widthHint = 180; + data.verticalIndent = 5; + experimentPanel.setLayoutData(data); + content.layout(); + + } else { + ExperimentPanel.open(this); + } + } + updateEnabledActions(); + updateWindowTitle(); + } + } + + + public Experiment getExperiment() { + return experiment; + } + + + public boolean isExperimentMode() { + return experiment != null; + } + + + public void setExperimentModeEmbedded(boolean on) { + this.experimentModeEmbedded = on; + } + + + public AppPrefs getPrefs() { + return getPrefsManager().getPrefs(); + } + + + AppPrefsManager getPrefsManager() { + if (prefsManager == null) { + prefsManager = new AppPrefsManager(this); + } + return prefsManager; + } + + + public Properties getProperties() { + return props; + } + + + public ActionManager getActions() { + if (actions == null) { + actions = new ActionManager(this); + } + return actions; + } + + + public Shell getShell() { + Shell shell = super.getShell(); + if (shell == null) { + return this.shell; + } + return shell; + } + + + public SegmentationView getView() { + return view; + } + + + public SegmentationContext getContext() { + if (view != null) { + return view.getContext(); + } + return null; + } + + + public File getContextFile() { + if (hasContextFile()) { + return getContext().getFile(); + } + return null; + } + + + public Image getIcon(String url) { + Image image = JFaceResources.getImage(url); + if (image == null) { + ImageRegistry registry = JFaceResources.getImageRegistry(); + ImageDescriptor descriptor = createImageDescriptor(url); + registry.put(url, descriptor); + image = registry.get(url); + } + return image; + } + + + public boolean hasContext() { + if (view != null) { + return view.getContext() != null; + } + return false; + } + + + public boolean hasContextFile() { + return getContext() != null ? getContext().hasContextFile() : false; + } + + + public void setEnableFeedback(boolean on) { + if (observer != null) { + observer.setEnabled(on); + } + } + + + public void setContext(SegmentationContext ctx) { + if (ctx != null) { + // Set a segmenter if one has not been set + if (!view.hasSegmenter()) { + SegmenterRegistry registry = SegmenterRegistry.getInstance(); + view.setSegmenter(registry.getDefault()); + } + } + + // Set context + view.setContext(ctx); + updateWindowTitle(); + } + + + public void status(String message) { + setStatus(message); + } + + + public void status(String format, Object... args) { + setStatus(String.format(format, args)); + } + + + public void status(Image image, String format, Object... args) { + StatusLineManager manager = getStatusLineManager(); + if (manager != null) { + manager.setMessage(image, String.format(format, args)); + } + } + + + public void status(AppStatus s, String format, Object... args) { + if (format == null) { + setStatus(null); + + } else { + StatusLineManager manager = getStatusLineManager(); + if (manager != null) { + if (s == AppStatus.Error) { + manager.setErrorMessage(s.getIcon(), String.format(format, args)); + } else { + manager.setMessage(s.getIcon(), String.format(format, args)); + } + } + } + } + + + public void drop(FileDropEvent evt) { + System.out.println("drop received"); + for (File f : evt.files()) { + if (isAcceptable(f)) { + getActions().get(OpenAction.class).open(f); + break; + } + } + } + + + public boolean isAcceptable(File file) { + String ext = FileUtils.getExtension(file); + return (ext.equals(".ctx") || SwtUtils.getImageFormat(file) != -1); + } + + + private void handleContextChanged(ContextChangedEvent evt) { + + // Remove listener from old context + if (evt.oldContext != null) { + evt.oldContext.removeAnnotationListener(annotationListener); + } + + // Add listener to new context + if (evt.newContext != null) { + evt.newContext.addAnnotationListener(annotationListener); + } + + // Update application state + updateEnabledActions(); + } + + + private void handleAnnotationPerformed(AnnotationEvent e) { + + // Update state of undo and redo menu items + updateUndoRedoActions(); + updateExportImageMapAction(); + } + + + private final ContextChangeListener contextListener = new ContextChangeListener() { + public void contextChanged(ContextChangedEvent evt) { + handleContextChanged(evt); + } + }; + + + private final AnnotationListener annotationListener = new AnnotationAdapter() { + public void annotationsChanged(AnnotationEvent e) { + handleAnnotationPerformed(e); + } + }; + + + private class ImageObserver implements MouseMoveListener, ContextChangeListener { + private static final String MONOSPACE_FONT = "Monospace"; + + private final ImageControl ctrl; + private ImageData image; + private boolean inside; + private boolean enabled; + + + public ImageObserver(SegmentationView view) { + view.addContextChangeListener(this); + view.getCanvas().addMouseMoveListener(this); + ctrl = view.getImageControl(); + contextChanged(view.getContext()); + FontDescriptor fd = FontDescriptor.createFrom(MONOSPACE_FONT, 8, SWT.NORMAL); + JFaceResources.getFontRegistry().put(MONOSPACE_FONT, fd.getFontData()); + } + + + public void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + status(null); + Font font = JFaceResources.getDefaultFont(); + getStatusLineManager().getControl().setFont(font); + this.inside = false; + this.enabled = enabled; + } + } + + + public boolean isEnabled() { + return enabled; + } + + + public void mouseMove(MouseEvent e) { + if (isEnabled()) { + Point pt = getImagePoint(e); + if (pt != null) { + inside(pt, getColor(pt)); + } else { + outside(); + } + } + } + + + private void outside() { + if (inside) { + status(null); + Font font = JFaceResources.getDefaultFont(); + getStatusLineManager().getControl().setFont(font); + inside = false; + } + } + + + private final Point getImagePoint(MouseEvent e) { + if (image != null) { + Point pt = new Point(e.x, e.y); + if (ctrl.imageContains(pt)) { + return ctrl.canvasToImage(pt); + } + } + return null; + } + + + private final RGB getColor(Point pt) { + + int pixel = image.getPixel(pt.x, pt.y); + return image.palette.getRGB(pixel); + } + + + private void inside(Point pt, RGB c) { + if (!inside) { + Font font = JFaceResources.getFont(MONOSPACE_FONT); + getStatusLineManager().getControl().setFont(font); + } + + status(AppStatus.Information, + "Location [%4d,%4d] Color [%3d,%3d,%3d]", + pt.x, pt.y, c.red, c.green, c.blue); + + inside = true; + } + + + public void contextChanged(ContextChangedEvent evt) { + contextChanged(evt.newContext); + } + + + public void contextChanged(SegmentationContext ctx) { + if (ctx != null) { + image = ctx.getImageData(); + } else { + image = null; + } + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/Application.java b/image_annotation/src/ie/dcu/apps/ist/Application.java new file mode 100644 index 0000000..bbd3b38 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/Application.java @@ -0,0 +1,110 @@ +package ie.dcu.apps.ist; + +import ie.dcu.util.*; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.util.*; + +import static ie.dcu.util.FileUtils.pathJoin; +import static ie.dcu.util.OsUtils.*; + +/** + * Base application. Contains information strings and methods to load + * application wide resources. + * + * @author Kevin McGuinness + */ +public class Application { + public static final String APP_ID = "ist"; + public static final String APP_NAME = "Interactive Segmentation and Annotation Tool"; + public static final String APP_VERSION = "1.3.4"; + public static final String APP_RESOURCE_DIR = "resources"; + + /** + * Default locations to look for plugins + */ + public static final String[] DEFAULT_PLUGIN_SEARCH_PATH + = defaultPluginSearchPath(); + + /** + * Loads a properties file from the application resource area + * (resources/config) + * + * @param name + * The properties file name. ".properties" will be appended if + * necessary. + * @return A Properties object + * @throws IOException + * If there is an error creating the Properties object. + */ + public static Properties loadProperties(String name) throws IOException { + if (!name.endsWith(".properties")) { + name = name + ".properties"; + } + + String path = FileUtils.pathJoin(APP_RESOURCE_DIR, "config", name); + File file = new File(path); + System.out.println("Path:" + file); + return PropsUtils.load(file); + } + + /** + * Returns the users plugins folder + */ + public static String userPluginsFolder() { + return pathJoin(userHome(), ".ist", "plugins"); + } + + /** + * Returns the applications base folder + */ + public static String applicationFolder() { + String apphome = System.getenv("IST_HOME"); + if (apphome != null) { + File file = new File(apphome); + if (file.isDirectory()) { + return file.getAbsolutePath(); + } + } + + ProtectionDomain domain = Application.class.getProtectionDomain(); + CodeSource source = domain.getCodeSource(); + URL location = source.getLocation(); + String path = location.getPath(); + try { + + path = URLDecoder.decode(path, "UTF-8"); + if (isWindows()) { + path = path.substring(1); + } + + path = new File(path).getParent(); + } catch (UnsupportedEncodingException e) { + path = System.getProperty("user.dir"); + } + + return path; + } + + public static String applicationPluginsFolder() { + return pathJoin(applicationFolder(), "plugins"); + } + + public static String osxPluginSearchPath() { + return pathJoin(userHome(), "Library", + "Application Support", APP_NAME, "Plugins"); + } + + private static String[] defaultPluginSearchPath() { + ArrayList paths = new ArrayList(); + paths.add(userPluginsFolder()); + if (isMacOSX()) { + paths.add(osxPluginSearchPath()); + } + paths.add(applicationPluginsFolder()); + return paths.toArray(new String[paths.size()]); + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/EvaluatorRegistry.java b/image_annotation/src/ie/dcu/apps/ist/EvaluatorRegistry.java new file mode 100644 index 0000000..d7bc7b4 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/EvaluatorRegistry.java @@ -0,0 +1,70 @@ +package ie.dcu.apps.ist; + +import ie.dcu.eval.*; + +import java.util.*; + +public class EvaluatorRegistry { + private static EvaluatorRegistry instance; + private final Set evaluators; + + private EvaluatorRegistry() { + evaluators = new LinkedHashSet(); + init(); + } + + + private void init() { + add(new ConfusionMatrixEvaluator()); + add(new BoundaryAccuracyEvaluator()); + } + + + public static EvaluatorRegistry getInstance() { + if (instance == null) { + instance = new EvaluatorRegistry(); + } + return instance; + } + + + public Evaluator find(String name) { + for (Evaluator e : evaluators) { + + // Check name + String ename = e.getName(); + if (ename.equals(name)) { + return e; + } + + // Check class name + String cname = e.getClass().getName(); + if (cname.equals(name)) { + return e; + } + + // Check simple name + String sname = e.getClass().getSimpleName(); + if (sname.equals(name)) { + return e; + } + } + + return null; + } + + + public void add(Evaluator evaluator) { + evaluators.add(evaluator); + } + + + public Set evaluators() { + return Collections.unmodifiableSet(evaluators); + } + + + public Iterator iterator() { + return evaluators.iterator(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/Main.java b/image_annotation/src/ie/dcu/apps/ist/Main.java new file mode 100644 index 0000000..d878efd --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/Main.java @@ -0,0 +1,119 @@ +package ie.dcu.apps.ist; + +import ie.dcu.plugin.*; +import ie.dcu.segment.Segmenter; + +import java.util.logging.Logger; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.*; + +/** + * Application launcher. + * + * @author Kevin McGuinness + */ +public class Main { + public static final Logger log = Logger.getLogger("Main"); + + /** + * Message to show when GDI+ is not installed. + */ + private static final String NO_GDIPLUS = + "Unable to load required library: GDI+ (gdiplus.dll)\n\n" + + "The GDI+ library needs to be installed before this program " + + "can be used on this platform. See the note at the end of " + + "the download page for further details."; + + + public static void main(String[] args) { + System.out.print(Application.DEFAULT_PLUGIN_SEARCH_PATH); + for (String s: Application.DEFAULT_PLUGIN_SEARCH_PATH) { + System.out.println(s); + } + + Display.setAppName(Application.APP_NAME); + check(); + loadPlugins(); + AppWindow window = new AppWindow(); + window.setBlockOnOpen(true); + window.open(); + System.exit(0); + } + + private static void loadPlugins() { + PluginManager manager = new PluginManager(); + for (String path : Application.DEFAULT_PLUGIN_SEARCH_PATH) { + manager.searchPath().add(path); + } + manager.loadPlugins(); + for (Plugin plugin : manager.plugins()) { + String classname = plugin.getMetadata("segmenter"); + if (classname != null) { + loadSegmenterPlugin(plugin, classname); + } + } + } + + private static void loadSegmenterPlugin(Plugin plugin, String classname) { + try { + Segmenter segmenter = (Segmenter) + plugin.loadObject(classname); + + SegmenterRegistry.getInstance().add(segmenter); + + } catch (PluginException e) { + log.severe("Unable to load plugin: " + e); + } + } + + public static boolean contains(String[] args, String argument) { + for (String arg : args) { + while (arg.startsWith("-")) { + arg = arg.substring(1); + } + + if (arg.equals(argument)) { + return true; + } + } + return false; + } + + private static void check() { + // Check for potential missing GDI+ on Win2K, NT, ME 98 + String os = System.getProperty("os.name"); + if (os.equals("Windows 2000") || + os.equals("Windows NT") || + os.equals("Windows ME") || + os.equals("Windows 98")) + { + try { + System.loadLibrary("gdiplus"); + } catch (RuntimeException e) { + exit(NO_GDIPLUS); + } + } + } + + static void exit(String message) { + Display display = Display.getDefault(); + Rectangle rect = display.getBounds(); + + Shell shell = display.getActiveShell(); + if (shell == null) { + shell = new Shell(display, SWT.NO_TRIM); + shell.setBounds(rect.width/2, rect.height/2, 0, 0); + shell.open(); + shell.setText("Error"); + } + + MessageBox box = new MessageBox(shell, SWT.ICON_ERROR); + box.setText("Error"); + box.setMessage(message); + box.open(); + display.dispose(); + System.exit(1); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/PainterRegistry.java b/image_annotation/src/ie/dcu/apps/ist/PainterRegistry.java new file mode 100644 index 0000000..7b437b0 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/PainterRegistry.java @@ -0,0 +1,46 @@ +package ie.dcu.apps.ist; + +import ie.dcu.segment.painters.*; + +import java.util.*; + +public class PainterRegistry { + private final LinkedHashMap painters; + + public PainterRegistry() { + painters = new LinkedHashMap(); + init(); + } + + + private void init() { + add(new CombinedPainter()); + add(new OriginalPainter()); + add(new MarkupPainter()); + add(new MaskPainter()); + add(new ForegroundOnlyPainter()); + add(new OutlineOverlayPainter()); + } + + + public void add(SegmentationPainter painter) { + painters.put(painter.getName(), painter); + } + + + public SegmentationPainter get(String painter) { + return painters.get(painter); + } + + + public Collection values() { + return painters.values(); + } + + + public void dispose() { + for (SegmentationPainter p : values()) { + p.dispose(); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/SegmenterRegistry.java b/image_annotation/src/ie/dcu/apps/ist/SegmenterRegistry.java new file mode 100644 index 0000000..f3fec09 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/SegmenterRegistry.java @@ -0,0 +1,107 @@ +package ie.dcu.apps.ist; + +import ie.dcu.segment.*; + +import java.util.*; + +/** + * Registry for segmenters. + * + * @author Kevin McGuinness + */ +public class SegmenterRegistry implements Iterable { + private static final String DEFAULT_SEGMENTER_NAME = "IgcSegmenter"; + private static SegmenterRegistry instance; + private final Set segmenters; + + private SegmenterRegistry() { + segmenters = new LinkedHashSet(); + init(); + } + + + private void init() { + // OutlineSegmenter is not very useful + // add(new OutlineSegmenter()); + + // Geodesic Active Contours is too sensitive to parameters + // for general use on natural images. + // add(new SeededGacSegmenter()); + + //add(new SrgSegmenter()); + //add(new IgcSegmenter()); + //add(new BptSegmenter()); + //add(new SioxSegmenter()); + //add(new SpatiogramSegmenter()); + } + + + public static SegmenterRegistry getInstance() { + if (instance == null) { + instance = new SegmenterRegistry(); + } + return instance; + } + + + public Segmenter find(String name) { + for (Segmenter s : segmenters) { + + // Check name + String ename = s.getName(); + if (ename.equals(name)) { + return s; + } + + // Check class name + String cname = s.getClass().getName(); + if (cname.equals(name)) { + return s; + } + + // Check simple name + String sname = s.getClass().getSimpleName(); + if (sname.equals(name)) { + return s; + } + } + + return null; + } + + + public boolean isDefault(Segmenter s) { + if (s != null) { + if (segmenters.size() == 1 && segmenters.contains(s)) { + return true; + } + return s.getClass().getSimpleName().equals(DEFAULT_SEGMENTER_NAME); + } + return false; + } + + + public Segmenter getDefault() { + for (Segmenter s : segmenters) { + if (isDefault(s)) { + return s; + } + } + return null; + } + + + public void add(Segmenter segmenter) { + segmenters.add(segmenter); + } + + + public Set segmenters() { + return Collections.unmodifiableSet(segmenters); + } + + + public Iterator iterator() { + return segmenters.iterator(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/AboutAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/AboutAction.java new file mode 100644 index 0000000..be5d61f --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/AboutAction.java @@ -0,0 +1,109 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.Application; +import ie.dcu.swt.layout.LayoutFactory; + +import org.eclipse.jface.dialogs.*; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.*; + + +/** + * Show an about dialog. + * + * @author Kevin McGuinness + */ +public class AboutAction extends AppAction { + + private AboutBox dialog; + + public AboutAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + if (dialog == null) { + dialog = new AboutBox(); + } + dialog.open(); + } + + + private class AboutBox extends Dialog { + + private static final String MESSAGE = + " Kevin McGuinness " + + "kevin.mcguinness@eeng.dcu.ie\n" + + " Center for Digital Video Processing\n" + + " Dublin City University.\n" + + " http://www.cdvp.dcu.ie"; + + + private Composite composite; + + + protected AboutBox() { + super(window); + } + + + @Override + protected Control createDialogArea(Composite parent) { + composite = new Composite(parent, SWT.NONE); + composite.setLayout(LayoutFactory.createGridLayout(5, 5)); + + String image = string("aboutImage"); + addImage(image); + addLabel(String.format("Version %s", Application.APP_VERSION), SWT.CENTER); + addLabel(" SWT Version " + swtVersionString(), SWT.CENTER); + addLabel(" Developed By:", SWT.LEFT); + addLink(MESSAGE); + + return composite; + } + + private String swtVersionString() { + return String.format("%d (%s)", SWT.getVersion(), SWT.getPlatform()); + } + + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, + IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + } + + + @Override + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText("About"); + } + + + private void addLink(String string) { + Link link = new Link(composite, 0); + link.setText(string); + link.setLayoutData(LayoutFactory.createGridData()); + } + + + private void addLabel(String text, int alignment) { + Label label = new Label(composite, 0); + label.setText(text); + label.setAlignment(alignment); + label.setLayoutData(LayoutFactory.createGridData()); + label.setFont(JFaceResources.getBannerFont()); + } + + private void addImage(String url) { + Label label = new Label(composite, 0); + label.setImage(window.getIcon(url)); + label.setLayoutData(LayoutFactory.createGridData()); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ActionManager.java b/image_annotation/src/ie/dcu/apps/ist/actions/ActionManager.java new file mode 100644 index 0000000..b1897f8 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ActionManager.java @@ -0,0 +1,133 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.*; +import ie.dcu.segment.Segmenter; +import ie.dcu.util.OsUtils; + +import java.io.IOException; +import java.util.*; +import java.util.logging.Logger; + +/** + * Manages application actions. + * + * @author Kevin McGuinness + */ +public class ActionManager { + private static final Logger log = Logger.getLogger("ActionManager"); + + private final AppWindow window; + private final Map actions; + private ArrayList segmenterActions; + private Properties props; + + + public ActionManager(AppWindow window) { + + this.window = window; + this.actions = new HashMap(); + + try { + this.props = Application.loadProperties(getPropertiesFile()); + } catch (IOException e) { + log.severe("Error loading action properties: " + + e.getLocalizedMessage()); + throw new RuntimeException(e); + } + + init(); + } + + + private String getPropertiesFile() { + return OsUtils.isMacOS() ? "actions.mac" : "actions"; + } + + + Properties getProperties() { + return props; + } + + + AppWindow getWindow() { + return window; + } + + + public void init() { + add(new OpenAction(this)); + add(new SaveAction(this)); + add(new SaveAsAction(this)); + add(new AboutAction(this)); + add(new CopyAction(this)); + add(new ExitAction(this)); + add(new ExportViewAction(this)); + add(new ExportImageMapAction(this)); + add(new ExportTransparentPNGAction(this)); + add(new HelpAction(this)); + add(new PreferencesAction(this)); + add(new PrintAction(this)); + add(new RedoAction(this)); + add(new UndoAction(this)); + add(new NextAction(this)); + add(new PreviousAction(this)); + add(new OpenExperimentAction(this)); + add(new ConfigureSegmenterAction(this)); + + // Add select segmenter actions + SegmenterRegistry registry = SegmenterRegistry.getInstance(); + for (Segmenter s : registry) { + add(new SelectSegmenterAction(this, registry, s)); + } + } + + + public void add(AppAction action) { + actions.put(action.id(), action); + } + + + public T get(Class clazz) { + return clazz.cast(actions.get(id(clazz))); + } + + + public T get(Class clazz, String id) { + return clazz.cast(actions.get(id)); + } + + + public void setEnabled(String id, boolean enabled) { + AppAction action = actions.get(id); + if (action != null) { + action.setEnabled(enabled); + } + } + + + public void setEnabled(Class clazz, boolean enabled) { + AppAction action = get(clazz); + if (action != null) { + action.setEnabled(enabled); + } + } + + + public Collection getSegmentationActions() { + if (segmenterActions == null) { + segmenterActions = new ArrayList(); + for (AppAction a : actions.values()) { + if (a instanceof SelectSegmenterAction) { + segmenterActions.add((SelectSegmenterAction) a); + } + } + } + + return Collections.unmodifiableCollection(segmenterActions); + } + + + private String id(Class clazz) { + return clazz.getName(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/AppAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/AppAction.java new file mode 100644 index 0000000..a5a11a8 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/AppAction.java @@ -0,0 +1,122 @@ +package ie.dcu.apps.ist.actions; + +import java.util.logging.*; + +import org.eclipse.jface.dialogs.*; +import org.eclipse.swt.graphics.*; + +import ie.dcu.apps.ist.*; + +/** + * Root class for all application actions. + * + * @author Kevin McGuinness + */ +public class AppAction extends ConfiguredAction { + + // Make easier for subclasses to set status + protected static final AppStatus Information = AppStatus.Information; + protected static final AppStatus Warning = AppStatus.Warning; + protected static final AppStatus Error = AppStatus.Error; + + // Logger + protected final Logger log = Logger.getLogger(getClass().getSimpleName()); + + // Window + protected final AppWindow window; + + // Action manager + protected final ActionManager manager; + + + public AppAction(ActionManager m) { + super(m.getProperties()); + manager = m; + window = m.getWindow(); + } + + + public AppAction(ActionManager m, int style) { + super(m.getProperties(), style); + manager = m; + window = m.getWindow(); + } + + + @Override + public void arm() { + super.arm(); + status(AppStatus.Information, getDescription()); + } + + + @Override + public void disarm() { + super.disarm(); + window.setStatus(null); + } + + + public String id() { + return getClass().getName(); + } + + + protected Image icon(String key) { + String prop = string(key); + if (prop != null) { + return window.getIcon(prop); + } + return null; + } + + + protected void error(String message) { + MessageDialog.openError(window.getShell(), "Error", message); + } + + + protected void error(String format, Object... args) { + error(String.format(format, args)); + } + + + protected void warning(String message) { + MessageDialog.openWarning(window.getShell(), "Warning", message); + } + + + protected void warning(String format, Object... args) { + warning(String.format(format, args)); + } + + + protected void info(String message) { + MessageDialog.openInformation(window.getShell(), "Information", message); + } + + + protected void info(String format, Object... args) { + info(String.format(format, args)); + } + + + protected void log(Level level, String message, Throwable th) { + log.log(level, message, th); + } + + + protected String property(String key) { + return properties.getProperty(key); + } + + + protected void status(AppStatus s, String format, Object... args) { + window.status(s, format, args); + } + + + protected void status(String format, Object... args) { + status(Information, format, args); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.java new file mode 100644 index 0000000..5221928 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ConfigureSegmenterAction.java @@ -0,0 +1,24 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.views.SegmentationView; + +/** + * Show the configure segmenter dialog. + * + * @author Kevin McGuinness + */ +public class ConfigureSegmenterAction extends AppAction { + + public ConfigureSegmenterAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + SegmentationView view = window.getView(); + if (view.canShowOptions()) { + view.showSegmenterOptions(); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ConfiguredAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ConfiguredAction.java new file mode 100644 index 0000000..24a33f0 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ConfiguredAction.java @@ -0,0 +1,169 @@ +package ie.dcu.apps.ist.actions; + +import java.net.*; +import java.util.*; +import java.util.logging.*; + +import org.eclipse.jface.action.*; +import org.eclipse.jface.resource.*; + +/** + * Hover Action configured from a properties file. + * + * @author Kevin McGuinness + */ +public class ConfiguredAction extends HoverAction { + private static final Logger log = Logger.getLogger("ConfiguredAction"); + + protected final Properties properties; + private final String id; + + + public ConfiguredAction(Properties properties) { + this(properties, null, AS_PUSH_BUTTON); + } + + + public ConfiguredAction(Properties properties, int style) { + this(properties, null, style); + } + + + public ConfiguredAction(Properties properties, String id, int style) { + super(null, style); + + assert (properties != null); + this.properties = properties; + + if (id != null) { + this.id = id; + } else { + this.id = getClass().getSimpleName(); + } + + configure(); + } + + + private void configure() { + + // Set id + setId(id); + + // Set text + setText(string("text")); + + // Set description + String description; + if ((description = string("description")) != null) { + setDescription(description); + } + + // Set tooltip + String tooltip; + if ((tooltip = string("tooltip")) != null) { + setToolTipText(tooltip); + } + + // Set accelerator + Integer keycode; + if ((keycode = accelerator("accelerator")) != null) { + setAccelerator(keycode); + } + + // Set images + ImageDescriptor im; + + if ((im = image("image")) != null) { + setImageDescriptor(im); + } + + if ((im = image("hoverImage")) != null) { + setHoverImageDescriptor(im); + } + + if ((im = image("disabledImage")) != null) { + setDisabledImageDescriptor(im); + } + + // Set status + Boolean status; + + if ((status = bool("checked") != null)) { + setChecked(status); + } + + if ((status = bool("enabled") != null)) { + setEnabled(status); + } + } + + + protected final Integer accelerator(String key) { + String value = string(key); + + if (value == null) { + return null; + } + + int code = Action.convertAccelerator(value); + + if (code == 0) { + log.warning("No accelerator: " + value); + return null; + } + + if (code == -1) { + log.warning("Not a valid accelerator keycode: " + value); + return null; + } + + return code; + } + + + protected final Boolean bool(String key) { + String value = string(key); + + if (value == null) { + return null; + } + + if (value.trim().equals("true")) { + return true; + } else if (value.trim().equals("false")) { + return false; + } else { + log.warning("Not a boolean value: " + value); + return null; + } + } + + + protected final ImageDescriptor image(String key) { + String url = string(key); + + if (url == null) { + return null; + } + + try { + return ImageDescriptor.createFromURL(new URL(url)); + + } catch (MalformedURLException e) { + log.warning("Invalid URL for image " + url); + return null; + } + } + + + protected final String string(String key) { + return properties.getProperty(key(key)); + } + + + protected final String key(String name) { + return String.format("action.%s.%s", id, name); + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/CopyAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/CopyAction.java new file mode 100644 index 0000000..1e7eb65 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/CopyAction.java @@ -0,0 +1,100 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.views.SegmentationView; +import ie.dcu.segment.SegmentationContext; +import ie.dcu.segment.painters.SegmentationPainter; +import ie.dcu.swt.*; + +import java.awt.Toolkit; +import java.awt.datatransfer.*; +import java.awt.image.BufferedImage; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +/** + * Copy view to clipboard. + * + * @author Kevin McGuinness + */ +public class CopyAction extends AppAction { + + public CopyAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + + if (window.hasContext()) { + copy(); + } + } + + + private void copy() { + setClipboard(paintContext()); + status("View copied to clipboard as image"); + } + + + private static BufferedImage convert(Image image) { + BufferedImage im = ImageConverter.convert(image.getImageData()); + image.dispose(); + return im; + } + + + public void setClipboard(Image image) { + ImageSelection selection = new ImageSelection(convert(image)); + Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard(); + clip.setContents(selection, null); + } + + + + private Image paintContext() { + SegmentationView view = window.getView(); + SegmentationContext ctx = window.getContext(); + SegmentationPainter painter = view.getPainter(); + ObservableImage im = createCompatibleImage(ctx); + painter.paint(ctx, im); + return im.getImage(); + } + + + private ObservableImage createCompatibleImage(SegmentationContext ctx) { + Image im = new Image(Display.getCurrent(), ctx.getBounds()); + return new ObservableImage(im); + } + + + private static class ImageSelection implements Transferable { + private BufferedImage image; + + + public ImageSelection(BufferedImage image) { + this.image = image; + } + + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { DataFlavor.imageFlavor }; + } + + + public boolean isDataFlavorSupported(DataFlavor flavor) { + return DataFlavor.imageFlavor.equals(flavor); + } + + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException + { + if (!DataFlavor.imageFlavor.equals(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + return image; + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ExitAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ExitAction.java new file mode 100644 index 0000000..e6ee5c2 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ExitAction.java @@ -0,0 +1,37 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.*; + +import org.eclipse.jface.dialogs.MessageDialog; + +/** + * Terminate the application. + * + * @author Kevin McGuinness + */ +public class ExitAction extends AppAction { + public ExitAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + if (confirmExit(window)) { + window.close(); + System.exit(0); + } + } + + public static boolean confirmExit(AppWindow window) { + boolean confirm = window.getPrefs().get( + Boolean.class, AppPrefs.Keys.CONFIRM_EXIT, true); + + if (confirm) { + return MessageDialog.openConfirm( + window.getShell(), "Confirm", + "Really exit the application?"); + } + + return true; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ExportImageMapAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ExportImageMapAction.java new file mode 100644 index 0000000..54c78f7 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ExportImageMapAction.java @@ -0,0 +1,82 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.dialogs.ExportDialog; +import ie.dcu.apps.ist.export.imagemap.*; +import ie.dcu.segment.*; +import ie.dcu.swt.ImageConverter; + +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.logging.Level; + +import org.eclipse.swt.program.Program; + +/** + * Export segmentation results as HTML image maps + * + * @author Kevin McGuinness + */ +public class ExportImageMapAction extends AppAction { + + public ExportImageMapAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + + if (window.hasContext()) { + + // Get options from user + ExportDialog dialog = new ExportDialog(window.getShell()); + ExportDialog.Result result = dialog.open(); + + if (result != null) { + + // Grab image and mask + SegmentationContext ctx = window.getContext(); + SegmentationMask mask = ctx.getMask(); + BufferedImage image = ImageConverter.convert(ctx.getImageData()); + + // Setup exporter + Exporter exporter = new Exporter(image, mask); + exporter.setEffect(result.effect); + exporter.setHtmlFile(result.html); + exporter.setImageFile(result.image); + exporter.setObjectLink(result.link); + exporter.setExportShape(result.shape); + exporter.setObjectDescription(result.description); + + // Export + try { + exporter.export(result.folder); + } catch (IOException e) { + handleError(e); + return; + } catch (ExportException e) { + handleError(e); + return; + } + + if (result.open) { + File file = new File(result.folder, result.html); + Program program = Program.findProgram(".html"); + if (program != null) { + program.execute(file.getAbsolutePath()); + } + } + } + } + } + + private void handleError(Exception e) { + log(Level.WARNING, "Error exporting view as HTML image map", e); + + // Show error dialog + error("Error exporting view as HTML image map: %s", + e.getLocalizedMessage()); + + // Set status message + status(Error, "Error exporting view as HTML image map"); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.java new file mode 100644 index 0000000..1315e33 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ExportTransparentPNGAction.java @@ -0,0 +1,148 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.segment.*; +import ie.dcu.swt.SwtUtils; +import ie.dcu.util.FileUtils; + +import java.io.*; +import java.util.logging.Level; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.FileDialog; + +public class ExportTransparentPNGAction extends AppAction { + +private static final String DEFAULT_SUFFIX = ".png"; + + private FileDialog dialog; + private String directory; + + public ExportTransparentPNGAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + if (window.hasContext()) { + File file = getFile(); + if (file != null) { + directory = file.getParent(); + export(file); + } + } + } + + + private void export(File file) { + ImageData im = createSemiTransparentImage(); + try { + SwtUtils.saveImage(im, file); + } catch (IOException e) { + handleError(file, e); + } + } + + + private ImageData createSemiTransparentImage() { + SegmentationContext ctx = window.getContext(); + ImageData image = ctx.getImageData(); + SegmentationMask mask = ctx.getMask(); + ImageData result = new ImageData(image.width, image.height, 32, image.palette); + + int[] pixels = new int[image.width]; + byte[] alphas = new byte[image.width]; + + for (int y = 0; y < image.height; y++) { + image.getPixels(0, y, image.width, pixels, 0); + + int offset = y * mask.cols; + for (int i = 0; i < mask.cols; i++) { + if (mask.values[i+offset] == SegmentationMask.BACKGROUND) { + alphas[i] = (byte) 0x00; + } else { + alphas[i] = (byte) 0xff; + } + } + + result.setPixels(0, y, image.width, pixels, 0); + result.setAlphas(0, y, image.width, alphas, 0); + } + + return result; + } + + + private void handleError(File file, IOException e) { + // Log + log(Level.WARNING, "Error exporting view as image", e); + + // Show error dialog + error("Error exporting view as image %s: %s", + file.getName(), e.getLocalizedMessage() + ); + + // Set status message + status(Error, "Error exporting view as image %s", file.getName()); + } + + + private File getFile() { + createExportDialog(); + + String path = dialog.open(); + if (path != null) { + return checkFile(path); + } + + return null; + } + + + private File checkFile(String path) { + File file = new File(path); + + if (SwtUtils.getImageFormat(file) != -1) { + return file; + } + + // Unknown file extension, so assume jpg + return new File(FileUtils.replaceExtension(path, DEFAULT_SUFFIX)); + } + + + private void createExportDialog() { + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.SAVE | SWT.SHEET); + dialog.setText("Export Transparent PNG"); + dialog.setFilterExtensions(new String[]{"*.png"}); + dialog.setFilterNames(new String[]{"PNG Images"}); + } + + dialog.setFileName(getFileName()); + + String dir = getDirectory(); + if (dir != null) { + // Unfortunately this is buggy on SWT/GTK and + // the filter path isin't always set :'-( + dialog.setFilterPath(dir); + } + } + + private String getFileName() { + File file = window.getContext().getFile(); + return FileUtils.replaceExtension(file.getName(), DEFAULT_SUFFIX); + } + + + private String getDirectory() { + if (directory == null) { + File folder = window.getContext().getFolder(); + directory = (folder != null) ? + directory = folder.getAbsolutePath() : null; + } + + return directory; + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/ExportViewAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/ExportViewAction.java new file mode 100644 index 0000000..a6ca7b5 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/ExportViewAction.java @@ -0,0 +1,162 @@ +package ie.dcu.apps.ist.actions; + + +import ie.dcu.apps.ist.views.SegmentationView; +import ie.dcu.segment.SegmentationContext; +import ie.dcu.segment.painters.SegmentationPainter; +import ie.dcu.swt.*; +import ie.dcu.util.FileUtils; + +import java.io.*; +import java.util.logging.Level; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.*; + + +/** + * Export current view as image. + * + * @author Kevin McGuinness + */ +public class ExportViewAction extends AppAction { + + private static final String DEFAULT_SUFFIX = ".png"; + + private FileDialog dialog; + private String directory; + + public ExportViewAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + if (window.hasContext()) { + File file = getFile(); + if (file != null) { + directory = file.getParent(); + export(file); + } + } + } + + + private void export(File file) { + Image im = paintContext(); + + try { + SwtUtils.saveImage(im, file); + status("View successfully exported as %s", file.getName()); + + } catch (IOException e) { + + handleError(file, e); + + } finally { + + im.dispose(); + } + } + + + private void handleError(File file, IOException e) { + // Log + log(Level.WARNING, "Error exporting view as image", e); + + // Show error dialog + error("Error exporting view as image %s: %s", + file.getName(), e.getLocalizedMessage() + ); + + // Set status message + status(Error, "Error exporting view as image %s", file.getName()); + } + + + private File getFile() { + createExportDialog(); + + String path = dialog.open(); + if (path != null) { + return checkFile(path); + } + + return null; + } + + + private File checkFile(String path) { + File file = new File(path); + + if (SwtUtils.getImageFormat(file) != -1) { + return file; + } + + // Unknown file extension, so assume jpg + return new File(FileUtils.replaceExtension(path, DEFAULT_SUFFIX)); + } + + + private void createExportDialog() { + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.SAVE | SWT.SHEET); + dialog.setText(property("ExportViewAction.dialog.text")); + dialog.setFilterExtensions(getFilters()); + dialog.setFilterNames(getFilterNames()); + } + + dialog.setFileName(getFileName()); + + String dir = getDirectory(); + if (dir != null) { + // Unfortunately this is buggy on SWT/GTK and + // the filter path isin't always set :'-( + dialog.setFilterPath(dir); + } + } + + private String getFileName() { + File file = window.getContext().getFile(); + return FileUtils.replaceExtension(file.getName(), DEFAULT_SUFFIX); + } + + + private String getDirectory() { + if (directory == null) { + File folder = window.getContext().getFolder(); + directory = (folder != null) ? + directory = folder.getAbsolutePath() : null; + } + + return directory; + } + + + + private String[] getFilters() { + return new String[] { property("ExportViewAction.dialog.filter.exts") }; + } + + + private String[] getFilterNames() { + return new String[] { property("ExportViewAction.dialog.filter.text") }; + } + + + private Image paintContext() { + SegmentationView view = window.getView(); + SegmentationContext ctx = window.getContext(); + SegmentationPainter painter = view.getPainter(); + ObservableImage im = createCompatibleImage(ctx); + painter.paint(ctx, im); + return im.getImage(); + } + + + private ObservableImage createCompatibleImage(SegmentationContext ctx) { + Image im = new Image(Display.getCurrent(), ctx.getBounds()); + return new ObservableImage(im); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/HelpAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/HelpAction.java new file mode 100644 index 0000000..6905dd6 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/HelpAction.java @@ -0,0 +1,126 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.swt.layout.LayoutFactory; + +import org.eclipse.swt.*; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +/** + * Show a help dialog. + * + * @author Kevin McGuinness + */ +public class HelpAction extends AppAction { + + private HelpBox dialog; + + + public HelpAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + if (dialog == null) { + dialog = new HelpBox(); + } + + if (dialog.available()) { + dialog.open(); + } else { + info("Help browser unavailable on this system"); + } + } + + + /** + * Help dialog box, just wraps a browser. + */ + private class HelpBox { + private Shell shell; + private Composite content; + private Browser browser; + + + public HelpBox() { + createShell(); + createContent(); + } + + + private void createShell() { + shell = new Shell(window.getShell(), SWT.SHELL_TRIM); + + // Prevent the shell from disposing on close + shell.addShellListener(new ShellAdapter() { + public void shellClosed(ShellEvent evt) { + evt.doit = false; + close(); + } + }); + + // Use a fill layout + shell.setLayout(new GridLayout()); + + // Set size and title + shell.setSize(640, 480); + shell.setText("Help"); + } + + + private void createContent() { + // Create content pane + content = new Composite(shell, SWT.BORDER); + content.setLayout(new FillLayout()); + content.setLayoutData(LayoutFactory.createGridData()); + + // Add browser widget + try { + browser = new Browser(content, SWT.NONE); + } catch (SWTError e) { + + // Log warning and return + log.warning("Browser unavailable: " + e.getMessage()); + browser = null; + return; + } + + // Go to help home page + home(); + } + + + public void home() { + String urlString = string("helpURL"); + if (urlString != null) { + go(urlString); + } else { + browser.setText("Help file is unavailable"); + } + } + + + public void go(String url) { + browser.setUrl(url); + } + + + public boolean available() { + return browser != null; + } + + + public void open() { + shell.setVisible(true); + } + + + public void close() { + shell.setVisible(false); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/HoverAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/HoverAction.java new file mode 100644 index 0000000..cf933bc --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/HoverAction.java @@ -0,0 +1,88 @@ +package ie.dcu.apps.ist.actions; + +import org.eclipse.jface.action.*; +import org.eclipse.jface.resource.*; + +/** + * Default implementation of {@link IHoverAction}. + * + * @author Kevin McGuinness + */ +public class HoverAction extends Action implements IHoverAction { + private boolean armed = false; + + /** + * Creates a new action with no text and no image. Configure the action later + * using the set methods. + */ + public HoverAction() { + super(); + } + + + /** + * Creates a new action with the given text and no image. + * + * @param text + * the string used as the text for the action, or null + * if there is no text + */ + public HoverAction(String text) { + super(text); + } + + + /** + * Creates a new action with the given text and image. + * + * @param text + * the action's text, or null if there is no text + * @param image + * the action's image, or null if there is no image + */ + public HoverAction(String text, ImageDescriptor image) { + super(text, image); + } + + + /** + * Creates a new action with the given text and style. + * + * @param text + * the action's text, or null if there is no text. + * @param style + * one of + * AS_PUSH_BUTTON, + * AS_CHECK_BOX, + * AS_DROP_DOWN_MENU, + * AS_RADIO_BUTTON, and + * AS_UNSPECIFIED. + */ + public HoverAction(String text, int style) { + super(text, style); + } + + + /** + * Sets the armed property to true. + */ + public void arm() { + armed = true; + } + + + /** + * Sets the armed property to false. + */ + public void disarm() { + armed = false; + } + + + /** + * Returns true if armed. + */ + public boolean isArmed() { + return armed; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/HoverMenuManager.java b/image_annotation/src/ie/dcu/apps/ist/actions/HoverMenuManager.java new file mode 100644 index 0000000..d3b051a --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/HoverMenuManager.java @@ -0,0 +1,149 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.widgets.ImageMenuManager; + +import java.net.URL; +import java.util.*; + +import org.eclipse.jface.action.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.widgets.*; + +/** + * Menu Manager that notifies instances of IHoverAction when they are armed and + * disarmed. + * + * @author Kevin McGuinness + */ +public class HoverMenuManager extends ImageMenuManager { + + /** + * Stores items that have hover listeners added to them. + */ + private final Set hoverItems = new HashSet(); + + + /** + * Create hover menu manager. + */ + public HoverMenuManager() { + init(); + } + + + /** + * Create hover menu manager with id and text. + * + * @param text + * the text for the menu, or null if none. + * @param imageURL + * the menu image url, or null. + */ + public HoverMenuManager(String text, URL imageURL) { + super(text, imageURL); + init(); + } + + + /** + * Create hover menu manager text. + * + * @param text + * the text for the menu, or null if none. + */ + public HoverMenuManager(String text) { + super(text); + init(); + } + + + private void init() { + addMenuListener(menuListener); + } + + + private void addHoverListeners() { + Menu menu = getMenu(); + for (MenuItem item : menu.getItems()) { + addHoverListener(item); + } + } + + + private void addHoverListener(MenuItem item) { + IHoverAction action = getHoverAction(item); + if (action != null) { + addHoverListener(item, action); + } + } + + + private void addHoverListener(MenuItem item, IHoverAction action) { + if (!hoverItems.contains(item)) { + item.addArmListener(new HoverListener(action)); + hoverItems.add(item); + } + } + + + private void disarmAll() { + Menu menu = getMenu(); + for (MenuItem item : menu.getItems()) { + disarm(item); + } + } + + + private void disarm(MenuItem item) { + IHoverAction action = getHoverAction(item); + if (action != null) { + disarm(action); + } + } + + + private void disarm(IHoverAction action) { + if (action.isArmed()) { + action.disarm(); + } + } + + + private IHoverAction getHoverAction(MenuItem item) { + Object data = item.getData(); + if (data instanceof ActionContributionItem) { + IAction action = ((ActionContributionItem) data).getAction(); + + if (action instanceof IHoverAction) { + return (IHoverAction) action; + } + } + return null; + } + + + private IMenuListener menuListener = new IMenuListener2() { + public void menuAboutToShow(IMenuManager manager) { + addHoverListeners(); + } + + public void menuAboutToHide(IMenuManager manager) { + disarmAll(); + } + }; + + + private final class HoverListener implements ArmListener { + private final IHoverAction action; + + public HoverListener(IHoverAction action) { + this.action = action; + } + + + public void widgetArmed(ArmEvent e) { + disarmAll(); + action.arm(); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/IHoverAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/IHoverAction.java new file mode 100644 index 0000000..cac72be --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/IHoverAction.java @@ -0,0 +1,34 @@ +package ie.dcu.apps.ist.actions; + +import org.eclipse.jface.action.*; + +/** + * Extension of IAction that supports arming when the mouse hovers over the + * item. This may be useful for displaying descriptions of menu items in the + * status bar, for example. To use hover actions the {@link HoverMenuManager} + * must be used. + * + * @author Kevin McGuinness + */ +public interface IHoverAction extends IAction { + + + /** + * Executed when the action is hovered/armed. + */ + public void arm(); + + + /** + * Executed when the action is de-hovered/disarmed. + */ + public void disarm(); + + + /** + * Query if the action is armed. + * + * @return true if armed. + */ + public boolean isArmed(); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/NextAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/NextAction.java new file mode 100644 index 0000000..900618b --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/NextAction.java @@ -0,0 +1,122 @@ +/** + * + */ +package ie.dcu.apps.ist.actions; + +import java.io.*; +import java.util.*; + +/** + * Jump to the next image in the directory + * + * @author Kevin McGuinness + */ +public class NextAction extends AppAction { + // Supported file extensions + static final String EXTENSIONS = "jpg;jpeg;png;gif;bmp;ctx"; + + private final java.io.FileFilter imageFilter; + + + public NextAction(ActionManager m) { + super(m); + + // Create an image file filter + imageFilter = new SimpleFileFilter(EXTENSIONS); + } + + + @Override + public void run() { + if (window.hasContext()) { + // Get currently opened file + File file = window.getContext().getFile(); + + // Find the one after & open it + File next = findNext(file); + if (next != null) { + manager.get(OpenAction.class).open(next); + } + } + } + + + private File findNext(File file) { + File dir = file.getParentFile(); + + if (dir != null) { + + // List the images + File[] files = dir.listFiles(imageFilter); + + // Need at least one + if (files.length > 1) { + + // Search for ourself... + for (int i = 0; i < files.length; i++) { + + if (files[i].equals(file)) { + + // Use next file (wrap back to first) + return files[(i+1)%files.length]; + } + } + } + } + + return null; + } + +} + +class SimpleFileFilter implements java.io.FileFilter { + + /** + * Holds the list of extensions. It is expected it will be quite + * short, so we use an ArrayList. + */ + private final ArrayList extensions; + + + /** + * Construct a simple file extension filter based on a semicolon separated + * list of file extensions. + * + * @param extensions + * A list of file extensions (ex. jpeg;jpg;png) + */ + public SimpleFileFilter(String extensions) { + String[] exts = extensions.split(";"); + this.extensions = new ArrayList(exts.length); + + for (String s : exts) { + if ((s = s.trim()).length() != 0) { + if (s.startsWith("*")) { + s = s.substring(1); + } + + if (!s.startsWith(".")) { + s = "." + s; + } + + this.extensions.add(s); + } + } + } + + + public boolean accept(File pathname) { + if (pathname.isFile()) { + String name = pathname.getName(); + + // List of extensions should be fairly short.. + for (String ext : extensions) { + if (name.endsWith(ext)) { + return true; + } + } + } + + return false; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/OpenAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/OpenAction.java new file mode 100644 index 0000000..823f176 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/OpenAction.java @@ -0,0 +1,110 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.*; +import ie.dcu.segment.SegmentationContext; + +import java.io.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +/** + * Open an image or segmentation context. + * + * @author Kevin McGuinness + */ +public class OpenAction extends AppAction { + + private FileDialog dialog; + + public OpenAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + File file = getFile(); + if (file != null) { + open(file); + } + } + + + public boolean open(File file) { + String name = file.getName(); + try { + if (SegmentationContext.isContextFile(file)) { + + // Load context + window.setContext(SegmentationContext.load(file)); + status("Opened segmentation context %s successfully", name); + } else { + + // Create context + window.setContext(SegmentationContext.create(file)); + status("Opened image file %s successfully", name); + } + + // Save history + AppRecentFiles.getInstance().add(file); + + // Ok + return true; + + } catch (IOException e) { + + handleError(file, e); + status(Warning, "Proplem opening file %s", name); + + return false; + } + } + + + private void handleError(File file, IOException e) { + + // Get appropriate message + String message = e.getCause() == null ? e.getLocalizedMessage() : + e.getCause().getLocalizedMessage(); + + // Log warning + log.warning(String.format( + "Unable to open %s\n Problem: %s", file, message + )); + + // Show warning dialog + warning("Unable to open %s:\n%s", file.getName(), message); + } + + + private void createOpenDialog() { + // Create dialog if necessary + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.OPEN | SWT.SHEET); + dialog.setText(property("OpenAction.dialog.text")); + dialog.setFilterExtensions(getFilters()); + dialog.setFilterNames(getFilterNames()); + } + } + + private String[] getFilters() { + return new String[] { property("OpenAction.dialog.filter.exts") }; + } + + private String[] getFilterNames() { + return new String[] { property("OpenAction.dialog.filter.text") }; + } + + + private File getFile() { + createOpenDialog(); + + String path = dialog.open(); + if (path != null) { + return new File(path); + } + + return null; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/OpenExperimentAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/OpenExperimentAction.java new file mode 100644 index 0000000..2e27dd4 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/OpenExperimentAction.java @@ -0,0 +1,98 @@ +/** + * + */ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.exp.*; + +import java.io.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +/** + * Open an experiment file and switch to experiment mode. + * + * @author Kevin McGuinness + */ +public class OpenExperimentAction extends AppAction { + + private FileDialog dialog; + + + public OpenExperimentAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + File file = getFile(); + if (file != null) { + open(file); + } + } + + + private void open(File file) { + try { + Experiment ex = ExperimentFactory.getInstance().load(file); + window.setExperiment(ex); + + } catch (IOException ex) { + handleError(file, ex); + } catch (FormatException ex) { + handleError(file, ex); + } + } + + + private void handleError(File file, Exception ex) { + // Get appropriate message + String mesg = ex.getMessage(); + + // Log warning + log.warning(String.format( + "Unable to open experiment file %s\n Problem: %s", file, mesg + )); + + // Show warning dialog + warning("Unable to open experiment file %s:\n%s", file.getName(), mesg); + } + + + private File getFile() { + createOpenDialog(); + + String path = dialog.open(); + if (path != null) { + return new File(path); + } + + return null; + } + + + private void createOpenDialog() { + // Create dialog if necessary + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.OPEN | SWT.SHEET); + dialog.setText(property("OpenExperimentAction.dialog.text")); + dialog.setFilterExtensions(getFilters()); + dialog.setFilterNames(getFilterNames()); + } + } + + private String[] getFilters() { + return new String[] { + property("OpenExperimentAction.dialog.filter.exts") + }; + } + + private String[] getFilterNames() { + return new String[] { + property("OpenExperimentAction.dialog.filter.text") + }; + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/OpenRecentAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/OpenRecentAction.java new file mode 100644 index 0000000..299117e --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/OpenRecentAction.java @@ -0,0 +1,25 @@ +package ie.dcu.apps.ist.actions; + +import java.io.*; + +/** + * Action to open a recent file. + * + * @author Kevin McGuinness + */ +public class OpenRecentAction extends AppAction { + private final File file; + + public OpenRecentAction(ActionManager m, File file) { + super(m); + this.file = file; + setText(file.getName()); + String path = file.getAbsolutePath(); + setToolTipText(path); + setDescription(String.format("Open file %s", path)); + } + + public void run() { + manager.get(OpenAction.class).open(file); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/PreferencesAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/PreferencesAction.java new file mode 100644 index 0000000..1ccfc38 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/PreferencesAction.java @@ -0,0 +1,24 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.dialogs.*; + +/** + * Show preferences dialog box. + * + * @author Kevin McGuinness + */ +public class PreferencesAction extends AppAction { + private PrefsDialog dialog; + + public PreferencesAction(ActionManager m) { + super(m); + } + + + public void run() { + if (dialog == null) { + dialog = new PrefsDialog(window); + } + dialog.open(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/PreviousAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/PreviousAction.java new file mode 100644 index 0000000..4bfb907 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/PreviousAction.java @@ -0,0 +1,67 @@ +/** + * + */ +package ie.dcu.apps.ist.actions; + +import java.io.*; + +/** + * Jump to previous file in current directory. + * + * @author Kevin McGuinness + */ +public class PreviousAction extends AppAction { + private final java.io.FileFilter imageFilter; + + + public PreviousAction(ActionManager m) { + super(m); + + // Create an image file filter + imageFilter = new SimpleFileFilter(NextAction.EXTENSIONS); + } + + + @Override + public void run() { + if (window.hasContext()) { + // Get currently opened file + File file = window.getContext().getFile(); + + // Find the one before & open it + File next = findPrevious(file); + if (next != null) { + manager.get(OpenAction.class).open(next); + } + } + } + + + + private File findPrevious(File file) { + File dir = file.getParentFile(); + + if (dir != null) { + + // List the images + File[] files = dir.listFiles(imageFilter); + + // Need at least one + if (files.length > 1) { + + // Search for ourself... + for (int i = 0; i < files.length; i++) { + + if (files[i].equals(file)) { + + // Use previous file (wrap back to last) + return files[(i > 0) ? i - 1 : files.length - 1]; + } + } + } + } + + return null; + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/PrintAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/PrintAction.java new file mode 100644 index 0000000..3645631 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/PrintAction.java @@ -0,0 +1,107 @@ +package ie.dcu.apps.ist.actions; + + +import ie.dcu.apps.ist.views.SegmentationView; +import ie.dcu.segment.SegmentationContext; +import ie.dcu.segment.painters.SegmentationPainter; +import ie.dcu.swt.ObservableImage; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.printing.*; +import org.eclipse.swt.widgets.Display; + +/** + * Send current view to printer. + * + * @author Kevin McGuinness + */ +public class PrintAction extends AppAction { + + private static final String JOB_NAME = "Interactive Segmentation Tool"; + private PrintDialog dialog; + + public PrintAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + if (window.hasContext()) { + status("Printing..."); + PrinterData data = getPrintData(); + if (data != null) { + print(data); + status("Printing complete"); + } else { + status(null); + } + } + } + + + private PrinterData getPrintData() { + createPrintDialog(); + return dialog.open(); + } + + + private void print(PrinterData data) { + Printer printer = new Printer(data); + try { + print(printer); + + } finally { + printer.dispose(); + } + } + + + private void print(Printer printer) { + if (printer.startJob(JOB_NAME)) { + if (printer.startPage()) { + GC gc = new GC(printer); + try { + paint(gc, printer); + } finally { + gc.dispose(); + } + printer.endPage(); + } + printer.endJob(); + } + } + + + private void paint(GC gc, Printer printer) { + Image im = paintContext(); + try { + gc.drawImage(im, 30, 30); + } finally { + im.dispose(); + } + } + + + private void createPrintDialog() { + if (dialog == null) { + dialog = new PrintDialog(window.getShell(), SWT.SHEET); + } + } + + + private Image paintContext() { + SegmentationView view = window.getView(); + SegmentationContext ctx = window.getContext(); + SegmentationPainter painter = view.getPainter(); + ObservableImage im = createCompatibleImage(ctx); + painter.paint(ctx, im); + return im.getImage(); + } + + + private ObservableImage createCompatibleImage(SegmentationContext ctx) { + Image im = new Image(Display.getCurrent(), ctx.getBounds()); + return new ObservableImage(im); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/RedoAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/RedoAction.java new file mode 100644 index 0000000..4e84880 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/RedoAction.java @@ -0,0 +1,23 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.views.SegmentationView; + + +/** + * Redo last annotation action. + * + * @author Kevin McGuinness + */ +public class RedoAction extends AppAction { + public RedoAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + SegmentationView view = window.getView(); + if (view.canRedo()) { + view.redo(); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/SaveAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/SaveAction.java new file mode 100644 index 0000000..e652d28 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/SaveAction.java @@ -0,0 +1,120 @@ +package ie.dcu.apps.ist.actions; + +import java.io.*; +import java.util.logging.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +/** + * Save segmentation context. + * + * @author Kevin McGuinness + */ +public class SaveAction extends AppAction { + + private FileDialog dialog; + + public SaveAction(ActionManager m) { + super(m); + } + + + @Override + public void run() { + + if (window.hasContext()) { + + File file; + if (window.hasContextFile()) { + file = window.getContextFile(); + } else { + file = getFile(); + } + + if (file != null) { + save(file); + } + } + } + + + private void createSaveDialog() { + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.SAVE | SWT.SHEET); + dialog.setText(property("SaveAction.dialog.text")); + dialog.setFilterExtensions(getFilters()); + dialog.setFilterNames(getFilterNames()); + + String dir = getDirectory(); + if (dir != null) { + dialog.setFilterPath(dir); + } + } + + dialog.setFileName(getFileName()); + } + + + private String getDirectory() { + File folder = window.getContext().getFolder(); + if (folder != null) { + return folder.getAbsolutePath(); + } + + return null; + } + + + private String getFileName() { + return window.getContext().getDefaultFilename(); + } + + + private String[] getFilters() { + return new String[] { property("SaveAction.dialog.filter.exts") }; + } + + + private String[] getFilterNames() { + return new String[] { property("SaveAction.dialog.filter.text") }; + } + + + private File getFile() { + createSaveDialog(); + + String path = dialog.open(); + if (path != null) { + return new File(path); + } + + return null; + } + + + private void save(File file) { + try { + window.getContext().save(file); + window.updateWindowTitle(); + status("Saved context %s successfully", file.getName()); + } catch (IOException e) { + handleError(file, e); + } + } + + + private void handleError(File file, IOException e) { + + // Log + log(Level.SEVERE, "Error saving file", e); + + // Show error dialog + error("Error saving segmentation context %s: %s", + file.getName(), e.getLocalizedMessage() + ); + + // Set status message + status(Error, "Error saving segmentation context %s", file.getName()); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/SaveAsAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/SaveAsAction.java new file mode 100644 index 0000000..7db1c26 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/SaveAsAction.java @@ -0,0 +1,112 @@ +package ie.dcu.apps.ist.actions; + +import java.io.*; +import java.util.logging.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; + +/** + * Save segmentation context as. + * + * @author Kevin McGuinness + */ +public class SaveAsAction extends AppAction { + + private FileDialog dialog; + + public SaveAsAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + if (window.hasContext()) { + File file = getFile(); + if (file != null) { + save(file); + } + } + } + + + private void createSaveDialog() { + if (dialog == null) { + dialog = new FileDialog(window.getShell(), SWT.SAVE | SWT.SHEET); + dialog.setText(property("SaveAction.dialog.text")); + dialog.setFilterExtensions(getFilters()); + dialog.setFilterNames(getFilterNames()); + + String dir = getDirectory(); + if (dir != null) { + dialog.setFilterPath(dir); + } + } + + dialog.setFileName(getFileName()); + } + + + private String getDirectory() { + File folder = window.getContext().getFolder(); + if (folder != null) { + return folder.getAbsolutePath(); + } + + return null; + } + + + + private String getFileName() { + return window.getContext().getDefaultFilename(); + } + + + private String[] getFilters() { + return new String[] { property("SaveAction.dialog.filter.exts") }; + } + + + private String[] getFilterNames() { + return new String[] { property("SaveAction.dialog.filter.text") }; + } + + + private File getFile() { + createSaveDialog(); + + String path = dialog.open(); + if (path != null) { + return new File(path); + } + + return null; + } + + + private void save(File file) { + try { + window.getContext().save(file); + window.updateWindowTitle(); + status("Saved context %s successfully", file.getName()); + } catch (IOException e) { + handleError(file, e); + } + } + + + private void handleError(File file, IOException e) { + + // Log + log(Level.SEVERE, "Error saving file", e); + + // Show error dialog + error("Error saving segmentation context %s: %s", + file.getName(), e.getLocalizedMessage() + ); + + // Set status message + status(Error, "Error saving segmentation context %s", file.getName()); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/SelectSegmenterAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/SelectSegmenterAction.java new file mode 100644 index 0000000..485ec8a --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/SelectSegmenterAction.java @@ -0,0 +1,60 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.SegmenterRegistry; +import ie.dcu.apps.ist.views.SegmentationView; +import ie.dcu.segment.Segmenter; + +/** + * Segmenter radio buttons. + * + * @author Kevin McGuinness + */ +public class SelectSegmenterAction extends AppAction { + private final Segmenter segmenter; + + public SelectSegmenterAction(ActionManager m, SegmenterRegistry r, Segmenter s) { + super(m, AS_RADIO_BUTTON); + this.segmenter = s; + setText(s.getName()); + setToolTipText(s.getDescription()); + + if (!s.isAvailable()) { + super.setEnabled(false); + setChecked(false); + + String naText = string("NA"); + setToolTipText(naText); + setDescription(naText); + } else { + setChecked(r.isDefault(s)); + } + } + + public void setEnabled(boolean enabled) { + // Disallow enabling unavailable segmentation algorithms + if (!enabled || (segmenter.isAvailable() && enabled)) { + super.setEnabled(enabled); + } + } + + + public Segmenter getSegmenter() { + return segmenter; + } + + + @Override + public String id() { + return segmenter.getClass().getName(); + } + + + @Override + public void run() { + if (isChecked()) { + SegmentationView view = window.getView(); + view.setSegmenter(segmenter); + } + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/actions/UndoAction.java b/image_annotation/src/ie/dcu/apps/ist/actions/UndoAction.java new file mode 100644 index 0000000..d5c67f2 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/actions/UndoAction.java @@ -0,0 +1,22 @@ +package ie.dcu.apps.ist.actions; + +import ie.dcu.apps.ist.views.SegmentationView; + +/** + * Undo last annotation. + * + * @author Kevin McGuinness + */ +public class UndoAction extends AppAction { + public UndoAction(ActionManager m) { + super(m); + } + + @Override + public void run() { + SegmentationView view = window.getView(); + if (view.canUndo()) { + view.undo(); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/controllers/AnnotationTool.java b/image_annotation/src/ie/dcu/apps/ist/controllers/AnnotationTool.java new file mode 100644 index 0000000..eb89bb0 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/controllers/AnnotationTool.java @@ -0,0 +1,314 @@ +package ie.dcu.apps.ist.controllers; + + + +import ie.dcu.segment.annotate.*; +import ie.dcu.swt.*; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.Canvas; + +public class AnnotationTool extends MouseMotionAdapter { + private final AnnotationManager manager; + private ImageControl view; + private Canvas canvas; + + private Annotation current; + private AnnotationType type; + private int lineWidth; + private Point last; + private boolean invert; + + + public AnnotationTool(AnnotationManager manager) { + this (manager, null); + } + + /** + * + * @param manager + * @param view + */ + public AnnotationTool(AnnotationManager manager, ImageControl view) { + assert (manager != null); + + this.manager = manager; + this.current = null; + this.lineWidth = 1; + this.type = AnnotationType.Foreground; + + attach(view); + } + + + /** + * + * @param view + */ + public void attach(ImageControl view) { + detach(); + + if (view != null) { + this.view = view; + canvas = view.getCanvas(); + canvas.addMouseListener(this); + canvas.addMouseMoveListener(this); + } + } + + + + public void detach() { + if (view != null) { + canvas.removeMouseListener(this); + canvas.removeMouseMoveListener(this); + view = null; + canvas = null; + current = null; + } + } + + + /** + * Returns the image control associated with the annotation tool. + */ + public ImageControl getView() { + return view; + } + + + /** + * @return the line width + */ + public int getLineWidth() { + return lineWidth; + } + + + /** + * @param width the line width to set + */ + public void setLineWidth(int width) { + assert (width > 0); + this.lineWidth = width; + } + + + /** + * @return the annotation type + */ + public AnnotationType getType() { + return type; + } + + + /** + * @param type the annotation type to set + */ + public void setType(AnnotationType type) { + assert (type != null); + this.type = type; + } + + + + public void begin(Point pt) { + + // Create a new annotation + current = new Annotation(getTypeForUse(), lineWidth, view.canvasToImage(pt)); + + // Give immediate visual feedback + feedback(pt); + + // Set last + last = pt; + } + + + public void append(Point pt) { + + // Update the annotation + current.add(view.canvasToImage(pt)); + + // Give immediate visual feedback + feedback(last, pt); + + // Set last + last = pt; + } + + + public void commit(Point pt) { + // Clip points to image bounds + clip(last, pt); + + // Add point to annotation + current.add(view.canvasToImage(pt)); + + // Add annotation to annotation manager + manager.add(current); + + // Give immediate visual feedback + feedback(last, pt); + + // Mark current annotation as null + current = null; + } + + + public void cancel() { + + // Repaint dirty area + view.repaint(current.getBounds()); + + // Discard annotation + current = null; + } + + + + @Override + public void mouseDown(MouseEvent e) { + if (view == null || !view.isEnabled()) { + current = null; + return; + } + + ObservableImage image = view.getImage(); + if (image != null) { + + if (e.button == 1 || e.button == 3) { + + Point pt = new Point(e.x, e.y); + + if (view.imageContains(pt)) { + + // Check for right mouse button or ctrl modifier + + if (e.button == 3 || (e.stateMask & SWT.CTRL) != 0) { + // Invert type + invert = true; + } else { + invert = false; + } + + if (current != null) { + // Cancel last action + cancel(); + } + + begin(pt); + } + } + } + + return; + } + + + @Override + public void mouseMove(MouseEvent e) { + if (view == null || !view.isEnabled()) { + current = null; + return; + } + + if (current != null) { + Point pt = new Point(e.x, e.y); + + if (view.imageContains(pt)) { + // Normal case: append + append(pt); + + } else { + // Line drawn outside image + + if (view.getImage() != null) { + + commit(pt); + + } else { + // No image: cancel + cancel(); + } + } + } + + } + + + @Override + public void mouseUp(MouseEvent e) { + if (view == null || !view.isEnabled()) { + current = null; + return; + } + + if (current != null) { + Point pt = new Point(e.x, e.y); + + if (view.imageContains(pt)) { + + // Normal case: commit + commit(pt); + } else { + + // Line drawn outside image + if (view.getImage() != null) { + + commit(pt); + } else { + + // No image + cancel(); + } + } + } + } + + + private void clip(Point p, Point q) { + SwtUtils.clip(view.getCanvasImageBounds(), p, q); + } + + + private GC createFeedbackGC() { + GC gc = new GC(canvas); + gc.setAntialias(SWT.ON); + gc.setLineCap(SWT.CAP_ROUND); + gc.setLineJoin(SWT.JOIN_ROUND); + gc.setLineWidth((int) Math.floor(lineWidth * view.getZoom())); + gc.setForeground(getTypeForUse().getColor()); + return gc; + } + + + private void feedback(Point p1) { + feedback(p1, new Point(p1.x+1, p1.y)); + } + + + private void feedback(Point p1, Point p2) { + GC gc = createFeedbackGC(); + gc.setClipping(getClip(p1, p2)); + gc.drawLine(p1.x, p1.y, p2.x, p2.y); + gc.dispose(); + } + + + private Rectangle getClip(Point p1, Point p2) { + int x1 = Math.min(p1.x, p2.x) - lineWidth; + int x2 = Math.max(p1.x, p2.x) + lineWidth; + int y1 = Math.min(p1.y, p2.y) - lineWidth; + int y2 = Math.max(p1.y, p2.y) + lineWidth; + Rectangle r = new Rectangle(x1, y1, x2 - x1, y2 - y1); + return r.intersection(view.getCanvasImageBounds()); + } + + + private AnnotationType getTypeForUse() { + return (invert) ? type.invert() : type; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/controllers/MouseMotionAdapter.java b/image_annotation/src/ie/dcu/apps/ist/controllers/MouseMotionAdapter.java new file mode 100644 index 0000000..70a08e4 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/controllers/MouseMotionAdapter.java @@ -0,0 +1,26 @@ +package ie.dcu.apps.ist.controllers; + +import org.eclipse.swt.events.*; +import org.eclipse.swt.widgets.*; + +public class MouseMotionAdapter extends MouseAdapter implements MouseMoveListener { + + public MouseMotionAdapter() { + } + + + public MouseMotionAdapter(Control control) { + add(control); + } + + + public void add(Control control) { + control.addMouseListener(this); + control.addMouseMoveListener(this); + } + + + public void mouseMove(MouseEvent e) { + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/dialogs/ExportDialog.java b/image_annotation/src/ie/dcu/apps/ist/dialogs/ExportDialog.java new file mode 100644 index 0000000..6807637 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/dialogs/ExportDialog.java @@ -0,0 +1,368 @@ +package ie.dcu.apps.ist.dialogs; + +import ie.dcu.apps.ist.export.imagemap.AreaShape; +import ie.dcu.apps.ist.export.imagemap.RolloverEffect; +import ie.dcu.swt.SwtUtils; +import ie.dcu.swt.layout.LayoutFactory; + +import java.io.File; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Dialog; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +public class ExportDialog extends Dialog { + + public class Result { + public final File folder; + public final String html; + public final String image; + public final String link; + public final String description; + public final AreaShape shape; + public final RolloverEffect effect; + public final boolean open; + + Result() { + folder = new File(folderText.getText().trim()); + html = htmlText.getText().trim(); + image = imageText.getText().trim(); + link = linkText.getText().trim(); + description = descriptionText.getText(); + shape = getShape(); + effect = getEffect(); + open = openButton.getSelection(); + } + }; + + // Result + private Result result; + + // Top level components + private Shell shell; + private Composite content; + private Composite widgets; + private Composite buttons; + + // Widgets + private Text folderText; + private Button browseButton; + private Text htmlText; + private Text imageText; + private Text linkText; + private Button effectCheck; + private Combo effectCombo; + private Button[] shapeRadios; + private Text descriptionText; + private Button openButton; + + // Dialog buttons + private Button cancelButton; + private Button exportButton; + + // Effects + private final String[] ROLLOVER_EFFECTS = { + "Outline Object", "Darken Background", "Highlight Object" + }; + + // Shapes + private final String[] SHAPES = { + "Polygon", "Rectangle", "Circle" + }; + + // Default title + private static final String TITLE = "Export HTML Image Map"; + + public ExportDialog(Shell shell) { + super(shell); + setText(TITLE); + } + + public ExportDialog(Shell shell, int style) { + super(shell, style); + setText(TITLE); + } + + public Result open() { + Shell parent = getParent(); + shell = new Shell(parent, SWT.DIALOG_TRIM | + SWT.APPLICATION_MODAL | SWT.SHEET); + shell.setText(getText()); + shell.setLayout(new FillLayout()); + + createUI(); + + shell.pack(); + + SwtUtils.center(parent, shell); + + shell.open(); + Display display = parent.getDisplay(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) display.sleep(); + } + + return result; + } + + private void createUI() { + // Overall layout + content = new Composite(shell, 0); + content.setLayout(LayoutFactory.createGridLayout(0, 0, 1, false)); + widgets = new Composite(content, 0); + widgets.setLayout(LayoutFactory.createGridLayout(10, 5, 3, false)); + widgets.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + hline(content); + buttons = new Composite(content, 0); + buttons.setLayout(LayoutFactory.createGridLayout(10, 5, 3, false)); + buttons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + + // Form content + label(widgets, "Export folder:"); + folderText = text(widgets, ""); + browseButton = button(widgets, "Browse..."); + label(widgets, "HTML file name:"); + htmlText = text(widgets, "imagemap.html"); + spacer(widgets); + label(widgets, "Image file name:"); + imageText = text(widgets, "image.png"); + spacer(widgets); + hline(widgets); + label(widgets, "Export shape:"); + shapeRadios = radios(widgets, SHAPES); + // TODO: Enable when available + shapeRadios[2].setEnabled(false); + spacer(widgets); + label(widgets, "Object link:"); + linkText = text(widgets, "http://"); + spacer(widgets); + label(widgets, "Object description:"); + descriptionText = text(widgets, ""); + spacer(widgets); + effectCheck = checkbox(widgets, "Rollover effect:", true, 1); + effectCombo = combo(widgets, ROLLOVER_EFFECTS); + spacer(widgets); + hline(widgets); + openButton = checkbox(widgets, + "Open image map in browser after export completes", true, 3); + + // Button bar + expander(buttons); + cancelButton = new Button(buttons, SWT.PUSH); + cancelButton.setLayoutData(layout4()); + cancelButton.setText("Cancel"); + exportButton = new Button(buttons, SWT.PUSH); + exportButton.setLayoutData(layout4()); + exportButton.setText("Export"); + + // Set the default button + shell.setDefaultButton(exportButton); + + // Add listeners + addListeners(); + } + + private void addListeners() { + + browseButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event e) { + String dir = getDirectory(); + if (dir != null) { + folderText.setText(dir); + } + } + }); + + cancelButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event e) { + result = null; + shell.dispose(); + } + }); + + exportButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event e) { + if (validate()) { + result = new Result(); + shell.dispose(); + } + } + }); + + effectCheck.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event e) { + effectCombo.setEnabled(effectCheck.getSelection()); + } + }); + } + + private boolean validate() { + + File folder = new File(folderText.getText()); + if (!folder.isDirectory()) { + validationError("The export folder must be specified"); + folderText.setFocus(); + return false; + } + + String htmlFile = htmlText.getText().trim(); + if (htmlFile.equals("")) { + validationError("The HTML file name cannot be empty"); + htmlText.setFocus(); + return false; + } + + String imageFile = imageText.getText().trim(); + if (imageFile.equals("")) { + validationError("The image file name cannot be empty"); + imageText.setFocus(); + return false; + } + + return true; + } + + private void validationError(String message) { + MessageBox box = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION); + box.setMessage(message); + box.setText("Invalid Input"); + box.open(); + } + + private AreaShape getShape() { + for (Button bt : shapeRadios) { + if (bt.getSelection()) { + return AreaShape.valueOf(bt.getText()); + } + } + return null; + } + + private RolloverEffect getEffect() { + RolloverEffect effect = null; + if (effectCheck.getSelection()) { + String text = effectCombo.getText(); + if (text.equals("Outline Object")) { + effect = RolloverEffect.outlineObject(); + } else if (text.equals("Darken Background")) { + effect = RolloverEffect.darkenBackground(); + } else if (text.equals("Highlight Object")) { + effect = RolloverEffect.brightenForeground(); + } + } + return effect; + } + + private String getDirectory() { + DirectoryDialog dialog = new DirectoryDialog(shell); + return dialog.open(); + } + + private Button[] radios(Composite parent, String ... items) { + Composite c = new Composite(parent, SWT.NONE); + c.setLayout(LayoutFactory.createGridLayout(5, 10, items.length, false)); + c.setLayoutData(layout2()); + + Button[] bts = new Button[items.length]; + for (int i = 0; i < bts.length; i++) { + bts[i] = new Button(c, SWT.RADIO); + bts[i].setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); + bts[i].setText(items[i]); + bts[i].setSelection(i == 0); + } + + return bts; + } + + private Label hline(Composite parent) { + Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); + GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false); + data.horizontalSpan = 3; + data.heightHint = 10; + label.setLayoutData(data); + return label; + } + + private Label spacer(Composite parent) { + Label label = new Label(parent, SWT.NONE); + label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + return label; + } + + private Label expander(Composite parent) { + Label label = new Label(parent, SWT.NONE); + label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + return label; + } + + private Combo combo(Composite parent, String ... items) { + Combo combo = new Combo(parent, SWT.READ_ONLY); + combo.setItems(items); + combo.setLayoutData(layout2()); + if (items.length > 0) { + combo.select(0); + } + return combo; + } + + private Button checkbox(Composite parent, String text, + boolean checked, int span) { + + Button bt = new Button(parent, SWT.CHECK); + bt.setText(text); + bt.setSelection(checked); + GridData data = layout1(); + data.horizontalSpan = span; + bt.setLayoutData(data); + return bt; + } + + private Label label(Composite parent, String text) { + Label label = new Label(parent, SWT.NONE); + label.setText(text); + label.setLayoutData(layout1()); + return label; + } + + private Text text(Composite parent, String value) { + Text text = new Text(parent, SWT.BORDER | SWT.SINGLE); + text.setText(value); + text.setLayoutData(layout2()); + return text; + } + + private Button button(Composite parent, String text) { + Button bt = new Button(parent, SWT.PUSH); + bt.setText(text); + bt.setLayoutData(layout3()); + return bt; + } + + private GridData layout1() { + return new GridData(SWT.LEFT, SWT.CENTER, false, false); + } + + private GridData layout2() { + return new GridData(SWT.FILL, SWT.CENTER, true, false); + } + + private GridData layout3() { + return new GridData(SWT.RIGHT, SWT.CENTER, false, false); + } + + private GridData layout4() { + return new GridData(SWT.RIGHT, SWT.BOTTOM, false, false); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/dialogs/PrefsDialog.java b/image_annotation/src/ie/dcu/apps/ist/dialogs/PrefsDialog.java new file mode 100644 index 0000000..9ea4eeb --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/dialogs/PrefsDialog.java @@ -0,0 +1,435 @@ +package ie.dcu.apps.ist.dialogs; + +import static ie.dcu.segment.annotate.AnnotationType.*; +import ie.dcu.apps.ist.*; +import ie.dcu.apps.ist.AppPrefs.Keys; +import ie.dcu.apps.ist.widgets.ColorSelector; +import ie.dcu.swt.SwtUtils; +import ie.dcu.swt.layout.LayoutFactory; + +import java.util.*; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +public class PrefsDialog { + private static final String APPLICATION = "application"; + private static final String STATUS = "status"; + private static final String VIEW = "view"; + private static final String TITLE = "title"; + private static final String GENERAL = "general"; + private static final String CLOSE = "close"; + private static final String RESET = "reset"; + private static final String TEXT = "text"; + private static final String ICON = "icon"; + private static final String TTIP = "ttip"; + private static final String EXPERIMENT = "experiment"; + + private final AppWindow window; + private final AppPrefs prefs; + private final Shell shell; + private final Composite content; + private final TabFolder folder; + private final Composite buttonBar; + private final Composite generalTab; + private final Button resetButton; + private final Button closeButton; + private final HashMap controls; + + private transient GridData gd; + + + public PrefsDialog(AppWindow window) { + this.window = window; + this.prefs = window.getPrefs(); + this.shell = createShell(); + this.content = createContent(); + this.folder = createTabFolder(); + this.buttonBar = createButtonBar(); + this.generalTab = createTab(GENERAL); + this.resetButton = createResetButton(); + this.closeButton = createCloseButton(); + this.controls = new HashMap(); + + createControls(); + loadPreferences(); + + SwtUtils.center(window.getShell(), shell); + } + + + private void createControls() { + // Initialize general tab + createBanner(generalTab, APPLICATION); + + createLabel(generalTab, Keys.CONFIRM_EXIT); + createCheckBox(generalTab, Keys.CONFIRM_EXIT); + + createBanner(generalTab, STATUS); + + createLabel(generalTab, Keys.ENABLE_FEEDBACK); + createCheckBox(generalTab, Keys.ENABLE_FEEDBACK); + + createBanner(generalTab, VIEW); + + createLabel(generalTab, Keys.FOREGROUND_COLOR); + createColorSelector(generalTab, Keys.FOREGROUND_COLOR); + + createLabel(generalTab, Keys.BACKGROUND_COLOR); + createColorSelector(generalTab, Keys.BACKGROUND_COLOR); + + createBanner(generalTab, EXPERIMENT); + + createLabel(generalTab, Keys.EXPERIMENT_EMBEDDED); + createCheckBox(generalTab, Keys.EXPERIMENT_EMBEDDED); + } + + + private void loadPreferences() { + + // Load confirm exit + loadBool(Keys.CONFIRM_EXIT, true); + + // Load enable feedback + loadBool(Keys.ENABLE_FEEDBACK, true); + + // Load foreground color + loadColor(Keys.FOREGROUND_COLOR, DEFAULT_FOREGROUND); + + // Load background color + loadColor(Keys.BACKGROUND_COLOR, DEFAULT_BACKGROUND); + + // Load experiment embedded + loadBool(Keys.EXPERIMENT_EMBEDDED, false); + } + + + private Shell createShell() { + Shell s = new Shell(window.getShell(), SWT.SHELL_TRIM | SWT.SHEET); + + // Add listener + s.addListener(SWT.Close, adapter); + + // Use a fill layout + s.setLayout(new FillLayout()); + + // Set size and title + s.setSize(420, 450); + s.setText(property(TITLE, "Preferences")); + + return s; + } + + + private Composite createContent() { + + // Create content pane with grid layout + Composite c = new Composite(shell, 0); + c.setLayout(new GridLayout()); + return c; + } + + + private TabFolder createTabFolder() { + // Create folder + TabFolder f = new TabFolder(content, SWT.TOP); + + // Layout folder + f.setLayoutData(LayoutFactory.createGridData()); + + return f; + } + + + private Composite createButtonBar() { + // Create bar + Composite bar = new Composite(content, SWT.NONE); + + // Layout bar + bar.setLayout(LayoutFactory.createGridLayout(0, 0, 2, true)); + bar.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false)); + + return bar; + } + + + private Composite createTab(String name) { + // Create item + TabItem item = new TabItem(folder, SWT.NONE); + item.setText(property(name, "tab", name)); + + // Create control + Composite control = new Composite(folder, SWT.NONE); + control.setLayout(LayoutFactory.createGridLayout(10, 5, 2, false)); + + // Set control + item.setControl(control); + + // Return control (item can be ignored) + return control; + } + + + private Button createResetButton() { + // Create button + Button bt = createButton(buttonBar, RESET, SWT.PUSH); + + // Layout button + gd = new GridData(SWT.LEFT, SWT.BOTTOM, true, false); + gd.minimumWidth = 85; + bt.setLayoutData(gd); + + // Done + return bt; + } + + + private Button createCloseButton() { + // Create button + Button bt = createButton(buttonBar, CLOSE, SWT.PUSH); + + // Layout button + gd = new GridData(SWT.RIGHT, SWT.BOTTOM, true, false); + gd.minimumWidth = 85; + bt.setLayoutData(gd); + + // Done + return bt; + } + + + private Button createCheckBox(Composite tab, String key) { + Button bt = new Button(tab, SWT.CHECK); + + // Layout button + gd = new GridData(SWT.RIGHT, SWT.CENTER, false, false); + gd.horizontalIndent = 5; + bt.setLayoutData(gd); + + // Add listener + bt.addListener(SWT.Selection, adapter); + bt.setData(key); + + // Put into controls + controls.put(key, bt); + + // Done + return bt; + } + + + private ColorSelector createColorSelector(Composite tab, String key) { + ColorSelector cs = new ColorSelector(tab); + + // Layout button + gd = new GridData(SWT.RIGHT, SWT.CENTER, false, false); + gd.horizontalIndent = 10; + gd.minimumWidth = 80; + cs.setLayoutData(gd); + + // Add listener + cs.addListener(SWT.Selection, adapter); + cs.setData(key); + + // Put into controls + controls.put(key, cs); + + // Done + return cs; + } + + + private Label createBanner(Composite tab, String key) { + // Create label + Label lb = new Label(tab, SWT.NONE); + lb.setText(property(key, key)); + lb.setFont(JFaceResources.getBannerFont()); + + // Layout label + gd = new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1); + gd.verticalIndent = 5; + lb.setLayoutData(gd); + + return lb; + } + + + private Label createLabel(Composite tab, String key) { + // Create label + Label lb = new Label(tab, SWT.NONE); + lb.setText(property(key, TEXT, key)); + + // Layout label + gd = new GridData(SWT.LEFT, SWT.CENTER, true, false); + gd.horizontalIndent = 5; + lb.setLayoutData(gd); + + return lb; + } + + + private Button createButton(Composite parent, String key, int style) { + Button bt = new Button(parent, style); + + // Configure button + configure(bt, key); + + // Add listener + bt.addListener(SWT.Selection, adapter); + + // Done + return bt; + } + + + protected void handleEvent(Event event) { + switch (event.type) { + case SWT.Close: + handleClose(event); + break; + case SWT.Selection: + handleSelection(event); + break; + } + + } + + + private void handleClose(Event event) { + // Prevent the shell from disposing on close + event.doit = false; + close(); + } + + + private void handleSelection(Event event) { + if (event.widget == closeButton) { + close(); + } else if (event.widget == resetButton) { + reset(); + } else { + Object data = event.widget.getData(); + if (data instanceof String) { + handleSelection(event, (String) data); + } + } + + } + + + private void handleSelection(Event event, String key) { + + if (key.equals(Keys.ENABLE_FEEDBACK)) { + storeBool(key); + + } else if (key.equals(Keys.FOREGROUND_COLOR)) { + + storeColor(key); + + } else if (key.equals(Keys.BACKGROUND_COLOR)) { + + storeColor(key); + + } else if (key.equals(Keys.EXPERIMENT_EMBEDDED)) { + + storeBool(key); + + } else if (key.equals(Keys.CONFIRM_EXIT)) { + + storeBool(key); + } + + } + + + private void storeBool(String key) { + Boolean bool = control(Button.class, key).getSelection(); + prefs.put(Boolean.class, key, bool); + } + + + private void storeColor(String key) { + RGB color = control(ColorSelector.class, key).getColor(); + if (color != null) { + prefs.put(RGB.class, key, color); + } + } + + + private void loadBool(String key, boolean def) { + Boolean bool = prefs.get(Boolean.class, key, def); + control(Button.class, key).setSelection(bool); + } + + + private void loadColor(String key, RGB def) { + RGB color = prefs.get(RGB.class, key, def); + control(ColorSelector.class, key).setColor(color); + } + + + public void reset() { + // Set defaults + prefs.clear(); + + // Reload + loadPreferences(); + } + + + public void close() { + shell.setVisible(false); + } + + + public void open() { + shell.setVisible(true); + } + + + private void configure(Button bt, String key) { + // Read properties + String text = property(key, TEXT, ""); + String icon = property(key, ICON, null); + String ttip = property(key, TTIP, null); + + // Set values + bt.setText(text); + bt.setImage(image(icon)); + bt.setToolTipText(ttip); + bt.setData(key); + } + + + private T control(Class clazz, String key) { + return clazz.cast(controls.get(key)); + } + + + private String property(String key, String def) { + Properties p = window.getProperties(); + return p.getProperty(String.format("PrefsDialog.%s", key), def); + } + + + private String property(String key, String type, String def) { + Properties p = window.getProperties(); + String prop = String.format("PrefsDialog.%s.%s", key, type); + return p.getProperty(prop, def); + } + + + private Image image(String url) { + return (url != null) ? window.getIcon(url) : null; + } + + + private final Listener adapter = new Listener() { + public void handleEvent(Event event) { + PrefsDialog.this.handleEvent(event); + } + }; +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/ContextChangeListener.java b/image_annotation/src/ie/dcu/apps/ist/event/ContextChangeListener.java new file mode 100644 index 0000000..8c617a0 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/ContextChangeListener.java @@ -0,0 +1,7 @@ +package ie.dcu.apps.ist.event; + +import java.util.*; + +public interface ContextChangeListener extends EventListener { + public void contextChanged(ContextChangedEvent evt); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/ContextChangedEvent.java b/image_annotation/src/ie/dcu/apps/ist/event/ContextChangedEvent.java new file mode 100644 index 0000000..638dad9 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/ContextChangedEvent.java @@ -0,0 +1,24 @@ +package ie.dcu.apps.ist.event; + + +import ie.dcu.segment.SegmentationContext; + +import java.util.*; + + +public class ContextChangedEvent extends EventObject { + private static final long serialVersionUID = 1L; + + public final SegmentationContext oldContext; + public final SegmentationContext newContext; + + public ContextChangedEvent( + Object source, + SegmentationContext oldContext, + SegmentationContext newContext + ) { + super(source); + this.oldContext = oldContext; + this.newContext = newContext; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/StateEvent.java b/image_annotation/src/ie/dcu/apps/ist/event/StateEvent.java new file mode 100644 index 0000000..38c5bbc --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/StateEvent.java @@ -0,0 +1,11 @@ +package ie.dcu.apps.ist.event; + +import java.util.*; + +public class StateEvent extends EventObject { + private static final long serialVersionUID = 1L; + + public StateEvent(Object source) { + super(source); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/StateListener.java b/image_annotation/src/ie/dcu/apps/ist/event/StateListener.java new file mode 100644 index 0000000..525f253 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/StateListener.java @@ -0,0 +1,13 @@ +/** + * + */ +package ie.dcu.apps.ist.event; + +import java.util.*; + +/** + * @author Kevin McGuinness + */ +public interface StateListener extends EventListener { + public void stateChanged(StateEvent evt); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/TickerEvent.java b/image_annotation/src/ie/dcu/apps/ist/event/TickerEvent.java new file mode 100644 index 0000000..ae6124f --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/TickerEvent.java @@ -0,0 +1,25 @@ +package ie.dcu.apps.ist.event; + + +import ie.dcu.apps.ist.widgets.Ticker; + +import java.util.EventObject; + +public class TickerEvent extends EventObject { + private static final long serialVersionUID = 755693053854019177L; + + private final long elapsed; + + public TickerEvent(Ticker t, long elapsed) { + super(t); + this.elapsed = elapsed; + } + + public Ticker getTicker() { + return (Ticker) getSource(); + } + + public long getElapsed() { + return elapsed; + } +}; diff --git a/image_annotation/src/ie/dcu/apps/ist/event/TickerListener.java b/image_annotation/src/ie/dcu/apps/ist/event/TickerListener.java new file mode 100644 index 0000000..26b6269 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/TickerListener.java @@ -0,0 +1,7 @@ +package ie.dcu.apps.ist.event; + +import java.util.*; + +public interface TickerListener extends EventListener { + public void tick(TickerEvent evt); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/TimeoutEvent.java b/image_annotation/src/ie/dcu/apps/ist/event/TimeoutEvent.java new file mode 100644 index 0000000..70659c3 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/TimeoutEvent.java @@ -0,0 +1,24 @@ +/** + * + */ +package ie.dcu.apps.ist.event; + + +import ie.dcu.apps.ist.widgets.SwtTimer; + +import java.util.EventObject; + +/** + * @author Kevin McGuinness + */ +public class TimeoutEvent extends EventObject { + private static final long serialVersionUID = 1L; + + public TimeoutEvent(SwtTimer source) { + super(source); + } + + public SwtTimer getTimer() { + return (SwtTimer) getSource(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/event/TimeoutListener.java b/image_annotation/src/ie/dcu/apps/ist/event/TimeoutListener.java new file mode 100644 index 0000000..c682041 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/event/TimeoutListener.java @@ -0,0 +1,13 @@ +/** + * + */ +package ie.dcu.apps.ist.event; + +import java.util.*; + +/** + * @author Kevin McGuinness + */ +public interface TimeoutListener extends EventListener { + public void timeoutOccured(TimeoutEvent evt); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/Experiment.java b/image_annotation/src/ie/dcu/apps/ist/exp/Experiment.java new file mode 100644 index 0000000..ef52005 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/Experiment.java @@ -0,0 +1,348 @@ +package ie.dcu.apps.ist.exp; + +import ie.dcu.eval.Evaluator; +import ie.dcu.apps.ist.*; +import ie.dcu.segment.Segmenter; + +import java.io.*; +import java.util.*; + +public class Experiment { + public static final int MAX_TASKS = 2500; + public static final int MAX_SEGMENTERS = 50; + public static final int MAX_EVALUATORS = 100; + + private String name; + private File dir; + private File outputFile; + private int time; + private List segmenters; + private List evaluators; + private List tasks; + private StorageSelection saveUserMasks; + private File saveUserMasksDir; + + Experiment() { + + } + + + public String getName() { + return name; + } + + + public File getDirectory() { + return dir; + } + + + public File getOutputFile() { + return outputFile; + } + + + public int getTime() { + return time; + } + + + public StorageSelection getStorageSelection() { + return saveUserMasks; + } + + + public File getStorageDirectory() { + return saveUserMasksDir; + } + + + public List getSegmenters() { + return Collections.unmodifiableList(segmenters); + } + + + public List getEvaluators() { + return Collections.unmodifiableList(evaluators); + } + + + public List getTasks() { + return Collections.unmodifiableList(tasks); + } + + + public boolean using(Segmenter segmenter) { + for (Segmenter s : segmenters) { + if (s.equals(segmenter)) { + return true; + } + } + return false; + } + + + public void load(File file) throws IOException, FormatException { + InputStream in = null; + try { + in = new FileInputStream(file); + load(in); + } finally { + if (in != null) { + in.close(); + } + } + } + + + public void load(InputStream in) throws IOException, FormatException { + + // Create properties object + Properties props = new Properties(); + props.load(in); + + // Load name + name = props.getProperty("name", "Experiment"); + + // Load directory + dir = getDir(props, "dir"); + + // Load output file + outputFile = getOutputFile(props); + + // Load task time + time = getTimeout(props, "time"); + + // Get save user masks selection + saveUserMasks = getStorageSelection(props, "save-user-masks"); + + // .. and directory + saveUserMasksDir = getUserMaskDir(props, "save-user-masks.dir"); + + // Load segmenters + segmenters = new ArrayList(); + for (int i = 1; i <= MAX_SEGMENTERS; i++) { + String key = "segmenters." + i; + String val = props.getProperty(key); + + if (val == null) { + break; + } + + Segmenter segmenter = findSegmenter(val); + if (segmenter != null) { + segmenters.add(segmenter); + } else { + exception("Segmenter not found: <%s>", val); + } + } + + // Load evaluators + evaluators = new ArrayList(); + for (int i = 1; i <= MAX_EVALUATORS; i++) { + String key = "evaluators." + i; + String val = props.getProperty(key); + + if (val == null) { + break; + } + + Evaluator evaluator = findEvaluator(val); + if (evaluator != null) { + evaluators.add(evaluator); + } else { + exception("Evaluator not found: <%s>", val); + } + } + + // Load tasks + tasks = new ArrayList(); + for (int i = 1; i <= MAX_TASKS; i++) { + + // Form keys + String imkey = String.format("task.%d.im", i); + String gtkey = String.format("task.%d.gt", i); + String desckey = String.format("task.%d.description", i); + + // Check if we're done + if (props.getProperty(imkey) == null) { + break; + } + + // Get image and ground truth file + File im = getFile(props, imkey); + File gt = getFile(props, gtkey); + + // Get description + String desc = getDescription(props, desckey); + + // Add task + tasks.add(new Task(im, gt, desc)); + } + } + + + private static StorageSelection getStorageSelection( + Properties props, String key + ) { + String prop = props.getProperty(key); + if (prop != null) { + StorageSelection selection = StorageSelection.parse(prop); + if (selection != null) { + return selection; + } + } + return StorageSelection.None; + } + + + private File getUserMaskDir(Properties props, String key) + throws FormatException { + + String fname = props.getProperty(key); + if (fname == null) { + fname = "user"; + } + + File file = new File(fname); + if (!file.isAbsolute()) { + file = new File(dir, fname); + } + + + if (!file.exists()) { + // Try and create it + if (!file.mkdirs()) { + String path = file.getAbsolutePath(); + exception("Couldn't create directory %s", path); + } + + } else if (!file.isDirectory()) { + String path = file.getAbsolutePath(); + exception("%s is not a directory", path); + } + + return file; + } + + + private static Segmenter findSegmenter(String name) { + return SegmenterRegistry.getInstance().find(name); + } + + + private static Evaluator findEvaluator(String name) { + return EvaluatorRegistry.getInstance().find(name); + } + + + private File getOutputFile(Properties props) { + String fname = props.getProperty("output", "output.txt"); + File file = new File(fname); + if (file.isAbsolute()) { + return file; + } + return new File(dir, fname); + } + + + + private static String getDescription(Properties props, String key) + throws FormatException { + + String desc = props.getProperty(key); + if (desc == null) { + exception("Expected description property for %s", key); + } + return desc; + } + + + private File getFile(Properties props, String key) + throws FormatException { + + String fname = props.getProperty(key); + + if (fname == null) { + exception("Expected file property for %s", key); + } + + File file = new File(fname); + if (file.isAbsolute()) { + if (file.isFile()) { + return file; + } + } + + file = new File(dir, fname); + if (!file.isFile()) { + exception("File not found %s", file.getAbsolutePath()); + } + + return file; + } + + + private static File getDir(Properties props, String key) + throws FormatException { + + String prop = props.getProperty(key); + if (prop == null) { + exception("Required directory element not found: %a",key); + } + + File file = new File(prop); + if (!file.isDirectory()) { + exception("%s is not a directory", prop); + } + + return file; + } + + + private static int getTimeout(Properties props, String key) + throws FormatException { + + String prop = props.getProperty(key, "120"); + try { + return Integer.parseInt(prop); + } catch (NumberFormatException ex) { + exception("%s is not an integer", key); + } + + return 0; + } + + + private static void exception(String format, Object ... args) + throws FormatException + { + throw new FormatException(String.format(format, args)); + } + + + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("name=<").append(name).append('>'); + sb.append(" dir=<").append(dir.getAbsolutePath()).append('>'); + sb.append(" output=<").append(outputFile.getAbsolutePath()).append('>'); + sb.append(" time=<").append(time).append('>'); + + // Evaluators + sb.append(" evaluators={ "); + for (Evaluator e : evaluators) { + sb.append(e.getName()).append(' '); + } + sb.append('}'); + + // Tasks + sb.append(" tasks={ "); + for (Task t : tasks) { + sb.append(t).append(' '); + } + sb.append('}'); + + return sb.toString(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentFactory.java b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentFactory.java new file mode 100644 index 0000000..802d14a --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentFactory.java @@ -0,0 +1,34 @@ +/** + * + */ +package ie.dcu.apps.ist.exp; + +import java.io.*; + +/** + * @author Kevin McGuinness + */ +public class ExperimentFactory { + private static ExperimentFactory instance; + + protected ExperimentFactory() { + + } + + public static ExperimentFactory getInstance() { + if (instance == null) { + instance = new ExperimentFactory(); + } + return instance; + } + + + public Experiment load(File file) + throws IOException, FormatException { + + Experiment ex = new Experiment(); + ex.load(file); + return ex; + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentManager.java b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentManager.java new file mode 100644 index 0000000..b03a6f7 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentManager.java @@ -0,0 +1,217 @@ +package ie.dcu.apps.ist.exp; + +import ie.dcu.eval.Evaluator; +import ie.dcu.segment.SegmentationMask; +import ie.dcu.segment.SegmentationContext; +import ie.dcu.apps.ist.AppWindow; +import ie.dcu.apps.ist.actions.*; + +import java.io.*; +import java.util.List; + +public class ExperimentManager { + + private final AppWindow window; + + private Experiment experiment; + private SegmentationContext activeContext; + private SegmentationMask referenceMask; + private ExperimentResults results; + private List tasks; + private Task activeTask; + private int progress; + private boolean aborted; + + + public ExperimentManager(AppWindow window) { + this.window = window; + } + + + public void beginExperiment(Experiment ex) throws IOException { + experiment = ex; + tasks = experiment.getTasks(); + activeContext = null; + activeTask = null; + progress = 0; + results = new ExperimentResults(experiment.getOutputFile()); + results.beginDocument(); + results.beginExperiment(experiment); + } + + + public void endExperiment() throws IOException { + results.endExperiment(); + results.flush(); + results.close(); + } + + + public void abortExperiment() { + results.endExperiment(); + results.close(); + aborted = true; + } + + + public void beginTask() throws IOException { + if (isComplete()) { + throw new IllegalStateException(); + } + + // Set active task + activeTask = tasks.get(progress); + + // Load context into main window + loadContext(); + + // Load reference mask + loadReferenceMask(); + + // Write task header to results + results.beginTask(progress, activeTask); + } + + + public void endTask() throws IOException { + + try { + // Save final mask + StorageSelection ss = experiment.getStorageSelection(); + if (ss != StorageSelection.None) { + store(String.format("task-%d-final.png", progress)); + } + + } finally { + + activeTask = null; + activeContext = null; + progress++; + results.endTask(); + results.flush(); + } + } + + + public boolean isComplete() { + return tasks.size() == progress || aborted; + } + + + public Experiment getExperiment() { + return experiment; + } + + + public String getTaskDescription() { + return activeTask.getDescription(); + } + + + public Task getActiveTask() { + return activeTask; + } + + + public int getTaskCount() { + return tasks.size(); + } + + + public int getProgress() { + return progress; + } + + + public SegmentationContext getActiveContext() { + return activeContext; + } + + + public List getEvaluators() { + return experiment.getEvaluators(); + } + + + public File getImageFile() { + return activeTask.getImageFile(); + } + + + public SegmentationMask getCurrentMask() { + return activeContext.getMask(); + } + + + public SegmentationMask getReferenceMask() { + return referenceMask; + } + + + public void evaluate(int elapsed) { + + if (!getCurrentMask().isUnknown()) { + SegmentationMask im = getCurrentMask(); + SegmentationMask gt = getReferenceMask(); + + results.beginEvaluation(elapsed); + + for (Evaluator evaluator : experiment.getEvaluators()) { + // Execute evaluator + evaluator.run(im, gt); + + // Add results + addResults(evaluator); + } + + results.endEvaluation(); + } + } + + + public void store(int elapsed) throws IOException { + // Save current mask + if (!getCurrentMask().isUnknown()) { + // Only save non empty masks + StorageSelection ss = experiment.getStorageSelection(); + if (ss == StorageSelection.All) { + store(String.format("task-%d-time-%d.png", progress, elapsed)); + } + } + } + + + private void store(String name) throws IOException { + SegmentationMask im = getCurrentMask(); + File dir = experiment.getStorageDirectory(); + File file = new File(dir, name); + im.save(file); + } + + + private void addResults(Evaluator evaluator) { + String[] measures = evaluator.getMeasures(); + for (String measure : measures) { + double value = evaluator.getMeasure(measure); + results.addMeasure(measure, value); + } + } + + + private void loadReferenceMask() throws IOException { + referenceMask = new SegmentationMask(activeContext.getBounds()); + referenceMask.load(activeTask.getMaskFile()); + } + + + private void loadContext() throws IOException { + ActionManager actions = window.getActions(); + OpenAction action = actions.get(OpenAction.class); + + if (!action.open(getImageFile())) { + throw new IOException("Unable to load image file"); + } + + activeContext = window.getContext(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentResults.java b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentResults.java new file mode 100644 index 0000000..2f54026 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/ExperimentResults.java @@ -0,0 +1,109 @@ +package ie.dcu.apps.ist.exp; + +import java.io.*; + +public class ExperimentResults { + private static final String XML_HEADER = + "%n%n"; + + private static final String BEGIN_EXPERIMENT_TAG = + "%n"; + + private static final String BEGIN_TASK_TAG = + " %n"; + + private static final String BEGIN_EVALUATION_TAG = + " %n"; + + private static final String MEASURE_TAG = + " %n"; + + private static final String END_EVALUATION_TAG = + " %n"; + + private static final String END_TASK_TAG = + " %n"; + + private static final String END_EXPERIMENT_TAG = + "%n"; + + + private final PrintStream out; + + + + ExperimentResults(File file) throws FileNotFoundException { + this(new FileOutputStream(file)); + } + + + ExperimentResults(OutputStream out) { + this.out = new PrintStream(new BufferedOutputStream(out)); + } + + + public void beginDocument() { + write(XML_HEADER); + } + + + public void beginExperiment(Experiment ex) { + String dir = ex.getDirectory().getAbsolutePath(); + write(BEGIN_EXPERIMENT_TAG, ex.getName(), ex.getTime(), dir); + } + + + public void beginTask(int idx, Task task) { + String im = task.getImageFile().getName(); + String gt = task.getMaskFile().getName(); + write(BEGIN_TASK_TAG, idx, im, gt); + } + + + public void beginEvaluation(int time) { + write(BEGIN_EVALUATION_TAG, time); + } + + + public void endEvaluation() { + write(END_EVALUATION_TAG); + } + + + public void addMeasure(String name, double value) { + write(MEASURE_TAG, name, value); + } + + + public void endTask() { + write(END_TASK_TAG); + } + + + public void endExperiment() { + write(END_EXPERIMENT_TAG); + } + + + public void endDocument() throws IOException { + flush(); + } + + + public void flush() throws IOException { + out.flush(); + if (out.checkError()) { + throw new IOException(); + } + } + + + public void close() { + out.close(); + } + + + private void write(String format, Object ... args) { + out.printf(format, args); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/FormatException.java b/image_annotation/src/ie/dcu/apps/ist/exp/FormatException.java new file mode 100644 index 0000000..8553f0d --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/FormatException.java @@ -0,0 +1,48 @@ +/** + * + */ +package ie.dcu.apps.ist.exp; + +/** + * @author Kevin McGuinness + */ +public class FormatException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + + /** + * + */ + public FormatException() { + } + + + /** + * @param message + */ + public FormatException(String message) { + super(message); + } + + + /** + * @param cause + */ + public FormatException(Throwable cause) { + super(cause); + } + + + /** + * @param message + * @param cause + */ + public FormatException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/StorageSelection.java b/image_annotation/src/ie/dcu/apps/ist/exp/StorageSelection.java new file mode 100644 index 0000000..aeae41f --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/StorageSelection.java @@ -0,0 +1,22 @@ +package ie.dcu.apps.ist.exp; + +/** + * Selection of what points in time to output the masks that + * the user has obtained so far in their interactions. + * + * @author Kevin McGuinness + */ +public enum StorageSelection { + All, + None, + Final; + + public static StorageSelection parse(String s) { + for (StorageSelection ss : values()) { + if (ss.toString().equalsIgnoreCase(s)) { + return ss; + } + } + return null; + } +}; diff --git a/image_annotation/src/ie/dcu/apps/ist/exp/Task.java b/image_annotation/src/ie/dcu/apps/ist/exp/Task.java new file mode 100644 index 0000000..754d4ed --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/exp/Task.java @@ -0,0 +1,39 @@ +package ie.dcu.apps.ist.exp; + +import java.io.*; + +public class Task { + + private final File imageFile; + private final File maskFile; + private final String description; + + + Task(File imFile, File gtFile, String desc) { + imageFile = imFile; + maskFile = gtFile; + description = desc; + } + + + public File getImageFile() { + return imageFile; + } + + + public File getMaskFile() { + return maskFile; + } + + + public String getDescription() { + return description; + } + + + public String toString() { + String im = imageFile.getAbsolutePath(); + String gt = maskFile.getAbsolutePath(); + return String.format("[ im=<%s> gt=<%s> desc=<%s> ]", im, gt, description); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/AreaShape.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/AreaShape.java new file mode 100644 index 0000000..11fc339 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/AreaShape.java @@ -0,0 +1,30 @@ +/** + * + */ +package ie.dcu.apps.ist.export.imagemap; + +/** + * The shape of the area tag for a HTML image map. + * + * @see HTML 4 DTD + * + * @author Kevin McGuinness + */ +public enum AreaShape { + Polygon("poly"), + Rectangle("rect"), + Circle("circle"); + + private String text; + + private AreaShape(String text) { + this.text = text; + } + + /** + * Returns the HTML shape attribute value. + */ + public String toString() { + return text; + } +} \ No newline at end of file diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ExportException.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ExportException.java new file mode 100644 index 0000000..51bf4c5 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ExportException.java @@ -0,0 +1,25 @@ +package ie.dcu.apps.ist.export.imagemap; + +/** + * Exception thrown when there is a problem producing the export. + * + * @author Kevin McGuinness + */ +public class ExportException extends Exception { + private static final long serialVersionUID = 1L; + + public ExportException() { + } + + public ExportException(String message) { + super(message); + } + + public ExportException(Throwable cause) { + super(cause); + } + + public ExportException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/Exporter.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/Exporter.java new file mode 100644 index 0000000..1ac92c8 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/Exporter.java @@ -0,0 +1,179 @@ +package ie.dcu.apps.ist.export.imagemap; + +import ie.dcu.image.ContourTracer; +import ie.dcu.segment.SegmentationMask; +import ie.dcu.util.FileUtils; + +import java.awt.Polygon; +import java.awt.image.*; +import java.io.*; +import java.util.*; + +import javax.imageio.ImageIO; + +/** + * Exports HTML image maps from a segmentation mask and an image. + * + * @author Kevin McGuinness + */ +public class Exporter { + private final SegmentationMask mask; + private final BufferedImage image; + + private RolloverEffect effect; + private String htmlFile = "imagemap.html"; + private String imageFile = "image.png"; + private String imageName = "image"; + private String objectDescription = ""; + private String objectLink = ""; + private AreaShape exportShape = AreaShape.Polygon; + + public Exporter(BufferedImage image, SegmentationMask mask) { + this.image = image; + this.mask = mask; + } + + public RolloverEffect getEffect() { + return effect; + } + + public void setEffect(RolloverEffect effect) { + this.effect = effect; + } + + public String getHtmlFile() { + return htmlFile; + } + + public void setHtmlFile(String htmlFile) { + this.htmlFile = htmlFile; + } + + public String getImageFile() { + return imageFile; + } + + public void setImageFile(String imageFile) { + this.imageFile = imageFile; + } + + public String getImageName() { + return imageName; + } + + public void setImageName(String imageName) { + this.imageName = imageName; + } + + public String getObjectDescription() { + return objectDescription; + } + + public void setObjectDescription(String description) { + this.objectDescription = description; + } + + public String getObjectLink() { + return objectLink; + } + + public void setObjectLink(String link) { + this.objectLink = link; + } + + public AreaShape getExportShape() { + return exportShape; + } + + public void setExportShape(AreaShape shape) { + this.exportShape = shape; + } + + public void export(File folder) throws IOException, ExportException { + ContourTracer tracer = new ContourTracer(mask, SegmentationMask.FOREGROUND); + List trace = tracer.trace(); + + if (trace.size() == 0) { + throw new ExportException("No objects found"); + } + + List preloads = getPreloads(trace); + + // Write image + ImageIO.write(image, "png", new File(folder, imageFile)); + + if (effect != null) { + int i = 1; + + // Generate effect images + for (Polygon object : trace) { + RenderedImage im = effect.createEffect(image, object); + File output = new File(folder, preloads.get(i++)); + ImageIO.write(im, "png", output); + } + } + + // Create image map + ImageMap map = new ImageMap(); + map.setImageHref(imageFile); + map.setImageName(imageName); + + // Add javascript preloads + for (String str : preloads) { + map.addPreload(str); + } + + // Add areas + int idx = 1; + for (Polygon polygon : trace) { + MapArea area = new MapArea(); + switch (exportShape) { + case Polygon: + area.setPolygon(polygon); + break; + case Rectangle: + area.setRect(polygon.getBounds()); + break; + case Circle: + // TODO: Implement circle! + + default: + area.setPolygon(polygon); + break; + } + + area.setAlt(objectDescription); + area.setHref(objectLink); + + if (effect != null) { + + + area.addAttr("onmouseover", + String.format("rollover(document.%s, image%s)", + imageName, idx++)); + area.addAttr("onmouseout", + String.format("rollover(document.%s, image0)", imageName)); + } + + map.addArea(area); + } + + map.exportToFile(new File(folder, htmlFile)); + } + + private List getPreloads(List trace) { + List preloads = new ArrayList(); + preloads.add(imageFile); + + if (effect != null) { + + String basename = FileUtils.removeExtension(imageFile); + for (int i = 0; i < trace.size(); i++) { + String filename = String.format("%s-%d.png", basename, i); + preloads.add(filename); + } + } + + return preloads; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/HtmlTag.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/HtmlTag.java new file mode 100644 index 0000000..7b0943e --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/HtmlTag.java @@ -0,0 +1,80 @@ +package ie.dcu.apps.ist.export.imagemap; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class used to generate HTML tags. + * + * @author Kevin McGuinness + */ +class HtmlTag { + final String name; + final Map attrs; + + HtmlTag(String name) { + this.name = name; + this.attrs = new HashMap(); + } + + HtmlTag attr(String name, String value) { + attrs.put(name, escape(value)); + return this; + } + + private static String escape(String value) { + return value.replaceAll("\"", "\\\\\""); + } + + static void indent(StringBuffer sb, int indent) { + for (int i = 0; i < indent; i++) { + sb.append(' '); + } + } + + void open(StringBuffer sb, int indent) { + open(sb, indent, false); + } + + void open(StringBuffer sb, int indent, boolean empty) { + + // Indent + indent(sb, indent); + + // Write start of tag + sb.append('<').append(name); + + // Write attributes + boolean first = true; + for (Map.Entry entry : attrs.entrySet()) { + + if (!first) { + sb.append('\n'); + indent(sb, indent + name.length() + 1); + } + + sb.append(' '); + sb.append(entry.getKey()); + sb.append('='); + sb.append('"'); + sb.append(entry.getValue()); + sb.append('"'); + + first = false; + } + + if (empty) { + sb.append('/'); + } + sb.append('>').append('\n'); + } + + void close(StringBuffer sb, int indent) { + indent(sb, indent); + sb.append('<'); + sb.append('/'); + sb.append(name); + sb.append('>'); + sb.append('\n'); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ImageMap.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ImageMap.java new file mode 100644 index 0000000..4125ccf --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/ImageMap.java @@ -0,0 +1,184 @@ +/** + * + */ +package ie.dcu.apps.ist.export.imagemap; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Exports HTML image maps + * + * @author Kevin McGuinness + */ +public class ImageMap { + private static final String TEMPLATE_FILE = "template.html"; + + private String pageTitle; + private String imageName; + private String imageHref; + private String imageAlt; + private String mapName; + private final List areas; + private final List preloads; + + public ImageMap() { + // set defaults : + this.pageTitle = "Image Map"; + this.imageName = "image"; + this.imageHref = ""; + this.imageAlt = ""; + this.mapName = "imagemap"; + this.areas = new LinkedList(); + this.preloads = new LinkedList(); + } + + public String getPageTitle() { + return pageTitle; + } + + public void setPageTitle(String pageTitle) { + assert (pageTitle != null); + this.pageTitle = pageTitle; + } + + public String getImageName() { + return imageName; + } + + public void setImageName(String imageName) { + this.imageName = imageName; + } + + public String getImageHref() { + return imageHref; + } + + public void setImageHref(String imageHref) { + assert (imageHref != null); + this.imageHref = imageHref; + } + + public String getImageAlt() { + return imageAlt; + } + + public void setImageAlt(String imageAlt) { + assert (imageAlt != null); + this.imageAlt = imageAlt; + } + + public String getMapName() { + return mapName; + } + + public void setMapName(String mapName) { + assert (mapName != null); + this.mapName = mapName; + } + + public void addArea(MapArea area) { + areas.add(area); + } + + public List areas() { + return areas; + } + + public void addPreload(String preload) { + this.preloads.add(preload); + } + + public List preloads() { + return preloads; + } + + public String export() { + String template = loadTemplate(); + + // Create preloads buffer + StringBuffer preloads = new StringBuffer(); + int idx = 0; + for (String s : preloads()) { + HtmlTag.indent(preloads, 8); + preloads.append("var image").append(idx); + preloads.append(" = new Image()\n"); + HtmlTag.indent(preloads, 8); + preloads.append("image").append(idx); + preloads.append(".src = '").append(s).append("'\n"); + idx++; + } + + StringBuffer contents = new StringBuffer(); + for (MapArea area : areas) { + contents.append('\n'); + area.export(contents, 8); + } + + Map subs = new HashMap(); + subs.put("image-name", imageName); + subs.put("page-title", pageTitle); + subs.put("image-href", imageHref); + subs.put("image-alt", imageAlt); + subs.put("map-name", mapName); + subs.put("contents", contents.toString()); + subs.put("preloads", preloads.toString()); + + return substitute(template, subs); + } + + public void exportToFile(File file) throws IOException { + FileWriter writer = new FileWriter(file); + try { + writer.append(export()); + } finally { + writer.close(); + } + } + + private String substitute(String template, Map subs) { + // This could be more efficient.. + String result = template; + for (String key : subs.keySet()) { + String regex = String.format("\\$\\{%s\\}", key); + result = result.replaceAll(regex, subs.get(key)); + } + + return result; + } + + private String loadTemplate() { + BufferedReader reader = new BufferedReader(new InputStreamReader( + getClass().getResourceAsStream(TEMPLATE_FILE))); + + StringBuffer buff = new StringBuffer(); + + try { + String line; + while ((line = reader.readLine()) != null) { + buff.append(line).append('\n'); + } + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + try { + reader.close(); + } catch (IOException e) { + // Ignore + } + } + + return buff.toString(); + } + + public String toString() { + return export(); + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/MapArea.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/MapArea.java new file mode 100644 index 0000000..2dc7003 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/MapArea.java @@ -0,0 +1,134 @@ +/** + * + */ +package ie.dcu.apps.ist.export.imagemap; + +import java.awt.Polygon; +import java.awt.Rectangle; +import java.net.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + + +public class MapArea { + + private String href; + private String alt; + private AreaShape shape; + private int[] coords; + private final Map attrs; + + public MapArea() { + href = ""; + alt = ""; + shape = AreaShape.Polygon; + coords = new int[0]; + attrs = new HashMap(); + } + + public void addAttr(String key, String value) { + attrs.put(key, value); + } + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href == null ? "" : href; + } + + public String getAlt() { + return alt; + } + + public void setAlt(String alt) { + this.alt = alt == null ? "" : alt; + } + + public AreaShape getShape() { + return shape; + } + + public void setShape(AreaShape shape) { + this.shape = shape == null ? AreaShape.Polygon : shape; + } + + public int[] getCoords() { + return coords; + } + + public void setCoords(int[] coords) { + this.coords = coords == null ? new int[0] : coords; + } + + public void setRect(Rectangle rect) { + setShape(AreaShape.Rectangle); + int[] coords = { + rect.x, + rect.y, + rect.x + rect.width, + rect.y + rect.height + }; + setCoords(coords); + } + + public void setCircle(int x, int y, int r) { + setShape(AreaShape.Circle); + int[] coords = { x, y, r }; + setCoords(coords); + } + + public void setPolygon(Polygon poly) { + setShape(AreaShape.Polygon); + int[] coords = new int[2*poly.npoints]; + for (int i = 0, j = 0; i < poly.npoints; i++) { + coords[j++] = poly.xpoints[i]; + coords[j++] = poly.ypoints[i]; + } + setCoords(coords); + } + + public void export(StringBuffer sb, int indent) { + String encodedHREF = href; + if (href.length() != 0) { + try { + URI uri = new URI(href); + URL url = uri.toURL(); + encodedHREF = url.toString(); + } catch (URISyntaxException e) { + // Ignore exceptions, the href string will be used instead + } catch (MalformedURLException e) { + // Ignore exceptions, the href string will be used instead + } + } + + HtmlTag tag = new HtmlTag("area"); + tag + .attr("href", encodedHREF) + .attr("alt", alt) + .attr("title", alt) + .attr("shape", shape.toString()) + .attr("coords", getCoordsString()); + + for (Entry entry : attrs.entrySet()) { + tag.attr(entry.getKey(), entry.getValue()); + } + + tag.open(sb, indent, true); + } + + public String getCoordsString() { + StringBuffer sb = new StringBuffer(); + boolean first = true; + for (int coord : coords) { + if (!first) { + sb.append(','); + } + sb.append(coord); + first = false; + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/RolloverEffect.java b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/RolloverEffect.java new file mode 100644 index 0000000..8e5ea2b --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/RolloverEffect.java @@ -0,0 +1,191 @@ +package ie.dcu.apps.ist.export.imagemap; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.Area; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; + +/** + * The roll-over effect for the image map. + * + * @author Kevin McGuinness + */ +public abstract class RolloverEffect { + + public abstract RenderedImage createEffect(Image image, Polygon object); + + public static RolloverEffect darkenBackground(float alpha) { + return new DarkenBackground(alpha); + } + + public static RolloverEffect darkenBackground() { + return new DarkenBackground(); + } + + public static RolloverEffect brightenForeground(float alpha) { + return new BrightenForeground(alpha); + } + + public static RolloverEffect brightenForeground() { + return new BrightenForeground(); + } + + public static RolloverEffect outlineObject() { + return new OutlineObject(); + } + + public static RolloverEffect outlineObject(Color color) { + return new OutlineObject(color); + } + + public static RolloverEffect outlineObject(Color color, Stroke stroke) { + return new OutlineObject(color, stroke); + } + + public static class DarkenBackground extends RolloverEffect { + + private float alpha = 0.5f; + + public DarkenBackground() { + + } + + public DarkenBackground(float alpha) { + this.alpha = alpha; + } + + public float getAlpha() { + return alpha; + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + @Override + public RenderedImage createEffect(Image image, Polygon object) { + int width = image.getWidth(null); + int height = image.getHeight(null); + BufferedImage im = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + + Area area = new Area(new Rectangle(0,0,width,height)); + area.subtract(new Area(object)); + + Graphics g = im.getGraphics(); + if (g instanceof Graphics2D) { + Graphics2D g2 = (Graphics2D) g; + g2.drawImage(image, 0, 0, null); + g2.setColor(new Color(0,0,0,alpha)); + g2.fill(area); + } + + return im; + } + } + + public static class BrightenForeground extends RolloverEffect { + + private float alpha = 0.5f; + + public BrightenForeground() { + + } + + public BrightenForeground(float alpha) { + this.alpha = alpha; + } + + public float getAlpha() { + return alpha; + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + @Override + public RenderedImage createEffect(Image image, Polygon object) { + int width = image.getWidth(null); + int height = image.getHeight(null); + BufferedImage im = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + + Graphics g = im.getGraphics(); + if (g instanceof Graphics2D) { + Graphics2D g2 = (Graphics2D) g; + g2.drawImage(image, 0, 0, null); + g2.setColor(new Color(1,1,1,alpha)); + g2.fill(object); + } + + return im; + } + } + + public static class OutlineObject extends RolloverEffect { + + private Color color = Color.white; + private Stroke stroke = new BasicStroke(2.0f, + BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + + public OutlineObject() { + + } + + public OutlineObject(Color color) { + this.color = color; + } + + public OutlineObject(Color color, Stroke stroke) { + this.color = color; + this.stroke = stroke; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public Stroke getStroke() { + return stroke; + } + + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + @Override + public RenderedImage createEffect(Image image, Polygon object) { + int width = image.getWidth(null); + int height = image.getHeight(null); + BufferedImage im = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + + Graphics g = im.getGraphics(); + if (g instanceof Graphics2D) { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.drawImage(image, 0, 0, null); + g2.setColor(color); + g2.setStroke(stroke); + g2.draw(object); + } + + return im; + } + } +} + diff --git a/image_annotation/src/ie/dcu/apps/ist/export/imagemap/template.html b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/template.html new file mode 100644 index 0000000..159d2a1 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/export/imagemap/template.html @@ -0,0 +1,38 @@ + + + + + + + + ${page-title} + + + + + + + ${image-alt} + + + +${contents} + + + \ No newline at end of file diff --git a/image_annotation/src/ie/dcu/apps/ist/recent/RecentFiles.java b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFiles.java new file mode 100644 index 0000000..16ef717 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFiles.java @@ -0,0 +1,234 @@ +package ie.dcu.apps.ist.recent; + +import java.io.*; +import java.util.*; +import java.util.logging.*; + +/** + * Class for managing a recent documents list. + * + * @author Kevin McGuinness + */ +public class RecentFiles implements Iterable { + public static final int DEFAULT_CAPACITY = 10; + + + private final LinkedList history; + private final List listeners; + private final File store; + private int capacity; + + + public RecentFiles(File store) { + + // Check the store is ok + checkValid(store); + + // Construct + this.history = new LinkedList(); + this.listeners = new LinkedList(); + this.store = store; + this.capacity = DEFAULT_CAPACITY; + + // Add a vm shutdown hook + addShutdownHook(); + } + + + public void setCapacity(int capacity) { + + // Check positive + if (capacity < 0) { + throw new IllegalArgumentException("capacity < 0"); + } + + if (this.capacity != capacity) { + this.capacity = capacity; + + // Capacity may have reduced + boolean reduced = history.size() > capacity; + while (history.size() > capacity) { + history.removeLast(); + } + + // Fire change + if (reduced) { + fireHistoryChanged(); + } + } + } + + + public int getCapacity() { + return capacity; + } + + + public void add(File file) { + if (!history.contains(file)) { + history.addFirst(file); + + // Check if we have exceeded capacity + if (history.size() > capacity) { + history.removeLast(); + } + + // Fire change + fireHistoryChanged(); + } + } + + + public void clear() { + if (!empty()) { + history.clear(); + fireHistoryChanged(); + } + } + + + public List files() { + return Collections.unmodifiableList(history); + } + + + public Iterator iterator() { + return files().iterator(); + } + + + public boolean empty() { + return history.isEmpty(); + } + + + public void load() throws IOException { + if (!store.exists()) { + // Create an empty store + store.createNewFile(); + } + + BufferedReader in = null; + try { + read(in = new BufferedReader(new FileReader(store))); + + } finally { + in.close(); + } + } + + + public void store() throws IOException { + PrintWriter out = null; + try { + write(out = new PrintWriter(store)); + + } finally { + out.close(); + } + } + + + public void addListener(RecentFilesListener rfl) { + listeners.add(rfl); + } + + + public void removeListener(RecentFilesListener rfl) { + listeners.remove(rfl); + } + + + protected static Logger getLogger() { + return Logger.getLogger(RecentFiles.class.getName()); + } + + + private void shutdown() { + try { + store(); + } catch (IOException e) { + getLogger().warning("Error writing recent documents to store: " + e); + } + } + + + private void fireHistoryChanged() { + RecentFilesEvent evt = null; + for (RecentFilesListener listener : listeners) { + if (evt == null) { + evt = new RecentFilesEvent(this); + listener.historyChanged(evt); + } + } + } + + + private void checkValid(File store) { + if (store == null) { + throw new IllegalArgumentException("store cannot be null"); + } + + if (store.isDirectory()) { + throw new IllegalArgumentException("store cannot be a directory"); + } + + if (store.isFile()) { + if (!store.canWrite()) { + throw new IllegalArgumentException("store not writable"); + } + } + } + + + private void addShutdownHook() { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + shutdown(); + } + }); + } + + + private void read(BufferedReader in) throws IOException { + + // Remember old history to check for changes + LinkedList old = new LinkedList(history); + + // Clear history if not empty + history.clear(); + + // Read files (discarding no longer existent ones) + String line; + while ((line = in.readLine()) != null) { + + // Stop when full + if (history.size() == capacity) { + break; + } + + File file = new File(line); + if (file.exists()) { + history.add(file); + } + } + + // Fire change if a change occurred + if (!history.equals(old)) { + fireHistoryChanged(); + } + } + + + private void write(PrintWriter out) throws IOException { + for (File file : history) { + String path = file.getAbsolutePath(); + out.println(path); + } + + // Check for errors + if (out.checkError()) { + throw new IOException("Error writing to file"); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesEvent.java b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesEvent.java new file mode 100644 index 0000000..8b0932e --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesEvent.java @@ -0,0 +1,17 @@ +package ie.dcu.apps.ist.recent; + +import java.util.*; + +public class RecentFilesEvent extends EventObject { + private static final long serialVersionUID = 1L; + + + public RecentFilesEvent(RecentFiles source) { + super(source); + } + + + public RecentFiles getList() { + return (RecentFiles) source; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesListener.java b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesListener.java new file mode 100644 index 0000000..8d50258 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/recent/RecentFilesListener.java @@ -0,0 +1,8 @@ +package ie.dcu.apps.ist.recent; + +import java.util.*; + +public interface RecentFilesListener extends EventListener { + + public void historyChanged(RecentFilesEvent evt); +} diff --git a/image_annotation/src/ie/dcu/apps/ist/views/ExperimentPanel.java b/image_annotation/src/ie/dcu/apps/ist/views/ExperimentPanel.java new file mode 100644 index 0000000..94dc2e3 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/views/ExperimentPanel.java @@ -0,0 +1,547 @@ +/** + * + */ +package ie.dcu.apps.ist.views; + +import ie.dcu.apps.ist.AppWindow; +import ie.dcu.apps.ist.event.*; +import ie.dcu.apps.ist.exp.*; +import ie.dcu.apps.ist.widgets.SwtTimer; +import ie.dcu.segment.annotate.*; + +import java.io.IOException; +import java.util.Properties; +import java.util.logging.*; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +/** + * @author Kevin McGuinness + */ +public class ExperimentPanel extends Composite { + + // Some property keys + private static final String DESCRIPTION_TITLE + = "description.title"; + + private static final String TIMEOUT_MESSAGE + = "timeout-message"; + + private static final String EXPERIMENT_COMPLETE_MESSAGE + = "experiment-complete-message"; + + // Type of button in use + private static enum ButtonType { Start, Finish }; + + // App window and experiment + private final AppWindow window; + private final Experiment experiment; + private final Logger log; + + // Controls + private final Group group; + private final Button button; + private final SwtTimer timer; + private final Text text; + + // Shell if the panel is inside a shell + private Shell shell; + + // Experiment manager + private final ExperimentManager manager; + private final EvaluationListener evaluator; + + + public ExperimentPanel(AppWindow window, Composite parent, int style) { + super(parent, style); + + // Set app window + this.window = window; + this.experiment = window.getExperiment(); + this.log = Logger.getLogger("Experiment"); + + + // Create controls + timer = new SwtTimer(this, SWT.NONE); + group = new Group(this, SWT.DEFAULT); + button = new Button(this, SWT.PUSH); + text = new Text(group, SWT.READ_ONLY | SWT.WRAP); + + // Create manager and evaluator + manager = new ExperimentManager(window); + evaluator = new EvaluationListener(manager, timer); + + // Setup controls + setupControls(); + layoutControls(); + addListeners(); + + // Begin experiment + beginExperiment(); + } + + + public static void open(AppWindow win) { + Shell app = win.getShell(); + + // Style bits + int style = SWT.MODELESS | SWT.RESIZE | SWT.BORDER | SWT.TITLE | SWT.CLOSE; + + // Construct shell + Shell shell = new Shell(app, style); + + // Determine shell bounds + Rectangle bounds = new Rectangle(0,0,180,280); + + // Move shell to an appropriate place (right of app window) + Rectangle rect = app.getBounds(); + bounds.x = rect.x + rect.width; + bounds.y = rect.y + rect.height / 2 - bounds.height / 2; + + // Move back if outside the screen + Rectangle screen = shell.getDisplay().getBounds(); + if (bounds.x + bounds.width > screen.width) { + bounds.x -= bounds.x + bounds.width - screen.width; + } + + // Set bounds + shell.setBounds(bounds); + + // Set layout + shell.setLayout(new FillLayout()); + + // Create experiment panel + final ExperimentPanel panel + = new ExperimentPanel(win, shell, SWT.NONE); + + panel.shell = shell; + panel.updateTitle(); + + // Add shell close button listener + shell.addShellListener(new ShellAdapter() { + public void shellClosed(ShellEvent e) { + if (!panel.manager.isComplete()) { + e.doit = panel.cancelExperiment(); + } + } + }); + + // Open experiment panel + shell.open(); + } + + + private void setupControls() { + group.setText(property(DESCRIPTION_TITLE)); + text.setEnabled(false); + enableButton(null); + } + + + private void layoutControls() { + GridLayout gl; + GridData gd; + + // Setup layout manager for this + gl = new GridLayout(); + gl.marginTop = 5; + gl.verticalSpacing = 5; + this.setLayout(gl); + + // Setup layout manager for group + gl = new GridLayout(); + group.setLayout(gl); + + // Layout timer + gd = new GridData(SWT.FILL, SWT.FILL, true, false); + timer.setLayoutData(gd); + + // Layout group + gd = new GridData(SWT.FILL, SWT.FILL, true, true); + group.setLayoutData(gd); + + // Layout button + gd = new GridData(SWT.FILL, SWT.FILL, true, false); + button.setLayoutData(gd); + + // Layout text + gd = new GridData(SWT.FILL, SWT.FILL, true, true); + text.setLayoutData(gd); + } + + + private void addListeners() { + button.addListener(SWT.Selection, buttonListener); + timer.addTimeoutListener(timeListener); + } + + + /** + * Function to call if something goes wrong with the experiment. Shows an + * error message and gracefully returns the application to normal mode. + * + * @param reason + * Short description of the problem. + */ + private void abortExperiment(String reason) { + timer.clear(); + evaluator.detach(); + + // Show abort message + error("Aborting experiment!%n%s", reason); + + // Tell manager of abort + manager.abortExperiment(); + + // Exit experiment mode + exitExperimentMode(); + } + + + /** + * Does what is necessary to exit experiment mode. + */ + private void exitExperimentMode() { + // Remove experiment view + window.setExperiment(null); + + // Re-enable interactions + window.getView().setEnabled(true); + + // Kill the shell + closeShell(); + } + + + /** + * Closes the encompassing (non-application) shell, if any. + */ + private void closeShell() { + if (shell != null) { + shell.close(); + shell.dispose(); + shell = null; + } + } + + + private boolean cancelExperiment() { + // Make sure the user knows what they're doing + boolean cancel = MessageDialog.openQuestion(window.getShell(), "Confirm", + "Are you sure you want to cancel the experiment?"); + + if (cancel) { + // Guess we better do what the user says, grumble grumble :-/ + timer.clear(); + evaluator.detach(); + manager.abortExperiment(); + window.setExperiment(null); + window.getView().setEnabled(true); + } + + return cancel; + } + + + /** + * Start the experiment. + */ + private void beginExperiment() { + try { + manager.beginExperiment(experiment); + + } catch (IOException e) { + log.log(Level.SEVERE, "Error starting experiment", e); + abortExperiment(e.getMessage()); + } + + // Begin first task + beginTask(); + } + + + /** + * Finish the experiment. + */ + private void endExperiment() { + try { + manager.endExperiment(); + + } catch (IOException e) { + log.log(Level.SEVERE, "Error ending experiment", e); + abortExperiment(e.getMessage()); + } + + // Tell user experiment is complete + notifyExperimentComplete(); + + // Stop experiment mode + exitExperimentMode(); + } + + + /** + * Load the task. + */ + private void beginTask() { + + // Set timer + timer.set(experiment.getTime()); + + // Begin task + try { + manager.beginTask(); + } catch (IOException e) { + log.log(Level.SEVERE, "Error starting task", e); + abortExperiment(e.getMessage()); + } + + // Disable interactions + window.getView().setEnabled(false); + + // Update description + text.setText(manager.getTaskDescription()); + + // Update title text + updateTitle(); + + // Setup start button + enableButton(ButtonType.Start); + + // Attach evaluator + evaluator.attach(); + } + + + private void updateTitle() { + if (shell != null) { + int ntasks = manager.getTaskCount(); + int progress = manager.getProgress(); + shell.setText(String.format("Task [%d/%d]", progress+1, ntasks)); + } + } + + + /** + * Start the task. + */ + private void startTask() { + + // Enable interactions + window.getView().setEnabled(true); + + // Start clock + timer.start(); + + // Enable finish button + enableButton(ButtonType.Finish); + } + + + /** + * End the task. + */ + private void endTask() { + // Detach evaluator + evaluator.detach(); + + // Evaluate final result + manager.evaluate(timer.getElapsed()); + + // End task + try { + manager.endTask(); + } catch (IOException e) { + log.log(Level.SEVERE, "Error ending task", e); + abortExperiment(e.getMessage()); + } + + // Stop and clear clock + timer.clear(); + } + + + /** + * End the current task and start the next. + * End the experiment if finished. + */ + private void nextTask() { + endTask(); + if (manager.isComplete()) { + endExperiment(); + } else { + beginTask(); + } + } + + + private void enableButton(ButtonType type) { + if (type != null) { + String name = type.toString().toLowerCase(); + button.setText(property("button.text." + name)); + button.setData(type); + button.setEnabled(true); + button.setFocus(); + } else { + button.setText(property("button.text.start")); + button.setEnabled(false); + button.setData(null); + } + } + + + private ButtonType getEnabledButton() { + return (ButtonType) button.getData(); + } + + + private void handleButtonClicked() { + ButtonType btn = getEnabledButton(); + switch (btn) { + case Start: + startTask(); + break; + case Finish: + // Prevent clicking finish by accident + if (manager.getActiveContext().getAnnotations().count() == 0) { + getDisplay().beep(); + } else { + nextTask(); + } + break; + } + } + + + private void handleTimeout() { + // Finish task + endTask(); + + // Tell user + notifyTimeoutOccurred(); + + // Next task + if (manager.isComplete()) { + endExperiment(); + } else { + beginTask(); + } + } + + + private void notifyExperimentComplete() { + message(EXPERIMENT_COMPLETE_MESSAGE); + } + + + private void notifyTimeoutOccurred() { + // BEEP BEEP!! time up :-) + getDisplay().beep(); + message(TIMEOUT_MESSAGE); + } + + + private void message(String key) { + Shell main = window.getShell(); + MessageDialog.openInformation(main, "Information", property(key)); + } + + + private void error(String message, Object ... args) { + Shell main = window.getShell(); + MessageDialog.openError(main, "Error", String.format(message, args)); + } + + + private String property(String key) { + return property(key, key); + } + + + private String property(String key, String def) { + Properties p = window.getProperties(); + return p.getProperty(String.format("ExperimentPanel.%s", key), def); + } + + + private final Listener buttonListener = new Listener() { + public void handleEvent(Event event) { + handleButtonClicked(); + } + }; + + + private final TimeoutListener timeListener = new TimeoutListener() { + public void timeoutOccured(TimeoutEvent evt) { + handleTimeout(); + } + }; +} + + +class EvaluationListener implements AnnotationListener { + private final ExperimentManager manager; + private final SwtTimer timer; + private final Logger log; + + public EvaluationListener(ExperimentManager manager, SwtTimer timer) { + this.manager = manager; + this.timer = timer; + this.log = Logger.getLogger("Experiment"); + } + + + public void attach() { + manager.getActiveContext().addAnnotationListener(this); + } + + + public void detach() { + manager.getActiveContext().removeAnnotationListener(this); + } + + + private void annotationsChanged(AnnotationEvent e) { + // Return if there is nothing to evaluate yet + if (e.manager.count() == 0) { + return; + } + + // Time the change occurred + int time = timer.getElapsed(); + manager.evaluate(time); + + // Store if necessary + try { + manager.store(time); + } catch (IOException ex) { + log.log(Level.SEVERE, "Error storing intermediate segmentation mask", e); + } + } + + + public void annotationPerformed(AnnotationEvent e) { + annotationsChanged(e); + } + + + public void annotationRedone(AnnotationEvent e) { + annotationsChanged(e); + } + + + public void annotationUndone(AnnotationEvent e) { + annotationsChanged(e); + } + + + public void annotationsCleared(AnnotationEvent e) { + annotationsChanged(e); + } +} + diff --git a/image_annotation/src/ie/dcu/apps/ist/views/SegmentationView.java b/image_annotation/src/ie/dcu/apps/ist/views/SegmentationView.java new file mode 100644 index 0000000..14a1bef --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/views/SegmentationView.java @@ -0,0 +1,994 @@ +package ie.dcu.apps.ist.views; + + +import ie.dcu.apps.ist.PainterRegistry; +import ie.dcu.apps.ist.event.*; +import ie.dcu.apps.ist.widgets.*; +import ie.dcu.segment.*; +import ie.dcu.segment.annotate.*; +import ie.dcu.segment.options.SegmenterOptionDialog; +import ie.dcu.segment.painters.SegmentationPainter; +import ie.dcu.swt.*; +import ie.dcu.swt.event.*; + +import java.lang.reflect.InvocationTargetException; +import java.net.*; +import java.util.Properties; +import java.util.logging.*; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.*; +import org.eclipse.jface.operation.*; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +public class SegmentationView extends Composite { + // Logger + private static final Logger log = Logger.getLogger(SegmentationView.class.getName()); + + + // Segmentation view properties + private final Properties props; + + + // Houses various painters + private final PainterRegistry painters; + + + // View handling annotations + private final AnnotatedImageControl view; + + + // Left, center and right tool bar + private final ToolBar bar1, bar2, bar3; + + + // Control to change brush size + private final BrushControl brushControl; + + + // Handles various events + private final EventHandler eventHandler; + + + // Proxy object to catch and log exceptions thrown by the segmentation + // algorithm, without crashing the application. + private RobustSegmenterProxy segmenterProxy; + + + // Auto segment on update flag + private boolean auto = true; + + + // Combo box housing the selectable views + private Combo combo; + + + // Current segmentation tool + private Segmenter segmenter; + + + // Context to run the segmentation in, (may be null) + private IRunnableContext runnableContext; + + + // Flag to indicate that the runnable context blocks when fork is true + private boolean blocksOnFork; + + + public enum Tool { + Foreground, + Background, + ZoomIn, + ZoomOut, + ZoomOriginal, + ZoomBestFit, + Repaint, + Undo, + Redo, + Clear, + SetBrushSize, + AutoApply, + Apply, + SetPainter, + SetLabel, + SegmenterOptions; + private ToolAction action; + }; + + + public SegmentationView(Properties props, Composite parent, int style) { + super(parent, style); + this.props = props; + + painters = new PainterRegistry(); + bar1 = new ToolBar(this, SWT.LEFT | SWT.FLAT); + bar2 = new ToolBar(this, SWT.RIGHT | SWT.FLAT); + bar3 = new ToolBar(this, SWT.CENTER | SWT.FLAT); + view = new AnnotatedImageControl(this, SWT.BORDER); + brushControl = new BrushControl(getShell(), SWT.BORDER); + eventHandler = new EventHandler(); + segmenterProxy = new RobustSegmenterProxy(); + + init(); + } + + + /** + * Initialize. + */ + private void init() { + initTools(); + createToolbar1(); + createToolbar2(); + createToolbar3(); + layoutControls(); + updatePainters(); + addListeners(); + updateToolStates(); + } + + + private void initTools() { + for (Tool t : Tool.values()) { + if (t.action == null) { + new ToolAction(t); + } + } + } + + + private void addListeners() { + brushControl.addSelectionListener(eventHandler); + view.addContextChangeListener(eventHandler); + view.addZoomListener(eventHandler); + addDisposeListener(eventHandler); + } + + + private void createToolbar1() { + ToolBarManager m = new ToolBarManager(bar1); + m.add(getAction(Tool.Foreground)); + m.add(getAction(Tool.Background)); + m.add(new Separator()); + m.add(getAction(Tool.ZoomIn)); + m.add(getAction(Tool.ZoomOut)); + m.add(getAction(Tool.ZoomOriginal)); + m.add(getAction(Tool.ZoomBestFit)); + m.add(new Separator()); + m.add(getAction(Tool.Repaint)); + m.add(getAction(Tool.Undo)); + m.add(getAction(Tool.Redo)); + m.add(getAction(Tool.Clear)); + m.add(new Separator()); + m.add(getAction(Tool.SetBrushSize)); + m.add(new Separator()); + m.add(getAction(Tool.AutoApply)); + m.add(getAction(Tool.Apply)); + m.add(getAction(Tool.SegmenterOptions)); + m.add(new Separator()); + m.update(true); + } + + + private void createToolbar2() { + SwtUtils.addLabel(bar2, getAction(Tool.SetPainter).getText()); + combo = SwtUtils.addCombo(bar2, 115, SWT.READ_ONLY); + combo.setToolTipText( getAction(Tool.SetPainter).getToolTipText()); + combo.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + execute(Tool.SetPainter, null); + } + }); + bar2.pack(); + } + + private void createToolbar3() { + SwtUtils.addLabel(bar3, getAction(Tool.SetLabel).getText()); + ToolItem item = new ToolItem(bar3, SWT.SEPARATOR); + Text text = new Text(bar3, SWT.SINGLE); + text.setToolTipText( getAction(Tool.SetLabel).getToolTipText()); + text.setText("Enter a Label"); + item.setWidth(100); + item.setControl(text); + text.pack(); + bar3.pack(); + + } + + + private void layoutControls() { + GridLayout layout = new GridLayout(3, false); + layout.marginHeight = 1; + layout.marginWidth = 2; + layout.horizontalSpacing = -1; // zero leaves 1 pixel gap + layout.verticalSpacing = 1; + setLayout(layout); + + Point sz1 = bar1.computeSize(SWT.DEFAULT, SWT.DEFAULT); + Point sz2 = bar2.computeSize(SWT.DEFAULT, SWT.DEFAULT); + Point sz3 = bar3.computeSize(SWT.DEFAULT, SWT.DEFAULT); + + int heightHintOfTwo = Math.max(sz1.y, sz2.y); + int heightHint = Math.max(heightHintOfTwo, sz3.y); + + // Layout toolbar 1 + GridData gd = new GridData(); + gd.grabExcessHorizontalSpace = true; + gd.horizontalAlignment = SWT.FILL; + gd.heightHint = heightHint; + bar1.setLayoutData(gd); + + // Layout toolbar 2 + gd = new GridData(); + gd.horizontalAlignment = SWT.FILL; + gd.heightHint = heightHint; + bar2.setLayoutData(gd); + + // Layout toolbar 3 + gd = new GridData(); + gd.horizontalAlignment = SWT.FILL; + gd.heightHint = heightHint; + bar3.setLayoutData(gd); + + // Layout view + gd = new GridData(); + gd.verticalIndent = 3; + gd.horizontalSpan = 3; + gd.grabExcessHorizontalSpace = true; + gd.grabExcessVerticalSpace = true; + gd.horizontalAlignment = SWT.FILL; + gd.verticalAlignment = SWT.FILL; + view.setLayoutData(gd); + } + + + private void updatePainters() { + combo.removeAll(); + boolean first = true; + for (SegmentationPainter p : painters.values()) { + combo.add(p.getName()); + if (first) { + combo.setText(p.getName()); + first = false; + } + } + } + + + public void setSegmenter(Segmenter segmenter) { + if (this.segmenter != segmenter) { + SegmentationContext ctx = view.getContext(); + + if (this.segmenter != null && ctx != null) { + // Finish old segmentation + segmenterProxy.finish(ctx); + + // Remove old segmenter + this.segmenter = null; + } + + // Set new segmenter + this.segmenter = segmenter; + + // Initialize a new segmentation + start(ctx); + + // Update enabled buttons + updateToolStates(); + } + } + + + public Segmenter getSegmenter() { + return segmenter; + } + + + public void setContext(SegmentationContext ctx) { + + // context change handler handles initialization etc. + // of segmenter and context + view.setContext(ctx); + } + + + public SegmentationContext getContext() { + return view.getContext(); + } + + + public void setAnnotationType(AnnotationType type) { + view.setAnnotationType(type); + } + + + public Canvas getCanvas() { + return view.getCanvas(); + } + + + public ImageControl getImageControl() { + return view.getImageControl(); + } + + + public void zoomIn() { + view.zoomIn(); + } + + + public void zoomOut() { + view.zoomOut(); + } + + + public void zoomOriginal() { + view.zoomOriginal(); + } + + + public void zoomBestFit() { + view.zoomBestFit(); + } + + + public void repaint() { + view.repaint(); + } + + + public void undo() { + view.undo(); + } + + + public void redo() { + view.redo(); + } + + + public void clear() { + view.clear(); + } + + + public void setBrushSize(int size) { + view.setLineWidth(size); + } + + + // For labeling the image + public void setLabel(SegmentationPainter painter) { + view.setPainter(painter); + combo.setText(painter.getName()); + } + + + public void setPainter(SegmentationPainter painter) { + view.setPainter(painter); + combo.setText(painter.getName()); + } + + + public SegmentationPainter getPainter() { + return view.getPainter(); + } + + + public void setAutoApply(boolean checked) { + if (!this.auto && checked) { + // Going from off to on, resegment now + apply(); + } + this.auto = checked; + } + + + public boolean isAutoApply() { + return this.auto; + } + + + public boolean canZoomIn() { + return view.canZoomIn(); + } + + + public boolean canZoomOut() { + return view.canZoomOut(); + } + + + public boolean canZoomOriginal() { + return view.canZoomOriginal(); + } + + + public boolean canZoomBestFit() { + return view.canZoomBestFit(); + } + + + public boolean canUndo() { + return view.canUndo(); + } + + + public boolean canRedo() { + return view.canRedo(); + } + + + public boolean canClear() { + return view.canClear(); + } + + + public boolean canShowOptions() { + if (hasSegmenter()) { + return segmenter.getOptions().size() > 0; + } + return false; + } + + + public Action getAction(Tool t) { + return t.action; + } + + + public void showSegmenterOptions() { + if (canShowOptions()) { + SegmenterOptionDialog dialog = + new SegmenterOptionDialog(getShell(), segmenter); + + int result = dialog.open(); + switch (result) { + case SegmenterOptionDialog.OK: + // Re-segment + apply(); + } + } + } + + + public boolean hasSegmenter() { + return segmenter != null; + } + + + public void performSegmentation(AnnotationEvent e, SegmentationContext ctx) { + + if (runnableContext == null) { + segment(e, ctx); + } else { + segmentWithRunnableContext(e, ctx); + } + + // Repaint everything + repaint(); + + } + + + public void setRunnableContext(IRunnableContext context, boolean blocksOnFork) { + runnableContext = context; + this.blocksOnFork = blocksOnFork; + } + + + public void addContextChangeListener(ContextChangeListener listener) { + view.addContextChangeListener(listener); + } + + + public void removeContextChangeListener(ContextChangeListener listener) { + view.removeContextChangeListener(listener); + } + + + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + bar1.setEnabled(enabled); + bar2.setEnabled(enabled); + view.setEnabled(enabled); + updateToolStates(); + } + + + private void run(boolean fork, IRunnableWithProgress runnable) { + boolean wasEnabled = isEnabled(); + + try { + // Disable interactions + setEnabled(false); + + // Run segmentation + runnableContext.run(fork, false, runnable); + + } catch (InvocationTargetException ex) { + + // Can't happen without a runtime exception occurring, so re-wrap + throw new RuntimeException(ex); + + } catch (InterruptedException ex) { + + // Can't happen because cancellable is false + throw new RuntimeException(ex); + + } finally { + setEnabled(wasEnabled); + } + } + + + private void segmentWithRunnableContext( + final AnnotationEvent e, + final SegmentationContext ctx) + { + // Fork if the segmenter is slow, and only if our runnable context blocks + boolean fork = blocksOnFork /*&& !segmenter.isFast()*/; + + run(fork, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + monitor.beginTask("Segmenting", IProgressMonitor.UNKNOWN); + try { + segment(e, ctx); + } finally { + monitor.done(); + } + } + }); + } + + + private void start(final SegmentationContext ctx) { + if (ctx != null && hasSegmenter()) { + if (runnableContext == null) { + + // No runnable context + segmenterProxy.init(ctx); + segmenterProxy.update(ctx); + + } else { + + // Run with progress monitor + run(blocksOnFork, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + monitor.beginTask("Initializing", IProgressMonitor.UNKNOWN); + try { + segmenterProxy.init(ctx); + segmenterProxy.update(ctx); + } finally { + monitor.done(); + } + } + }); + + } + + // Repaint everything + repaint(); + } + } + + + private void segment(AnnotationEvent e, SegmentationContext ctx) { + if (hasSegmenter()) { + if (e == null) { + // No event triggered this, so do a full update + segmenterProxy.update(ctx); + + } else { + // Call appropriate segmenter function + switch (e.type) { + case Cleared: + segmenterProxy.update(ctx); + break; + + case Undone: + segmenterProxy.removed(ctx, e.annotation); + break; + + case Redone: + case Added: + segmenterProxy.added(ctx, e.annotation); + break; + + default: + throw new RuntimeException(); + } + } + } + } + + + private boolean isAnnotatingBackground() { + return view.isAnnotatingBackground(); + } + + + private boolean isAnnotatingForeground() { + return view.isAnnotatingForeground(); + } + + + private String getText(Tool a) { + return props.getProperty(getKey(a, "text")); + } + + + private String getToolTip(Tool a) { + return props.getProperty(getKey(a, "tooltip")); + } + + + private ImageDescriptor getImage(Tool a) { + String location = props.getProperty(getKey(a, "image")); + if (location != null) { + if (location.length() == 0) { + return null; + } + + try { + return ImageDescriptor.createFromURL(new URL(location)); + } catch (MalformedURLException e) { + return null; + } + } + + return null; + } + + + private static String getKey(Tool action, String object) { + return String.format("SegmentationView.Action.%s.%s", action, object); + } + + + private int getType(Tool a) { + switch (a) { + case Foreground: + case Background: + case AutoApply: + return Action.AS_CHECK_BOX; + case SetBrushSize: + return Action.AS_DROP_DOWN_MENU; + case SetPainter: + return Action.AS_UNSPECIFIED; + default: + return Action.AS_PUSH_BUTTON; + } + } + + + private void execute(Tool a, Event e) { + switch (a) { + case Foreground: + setAnnotationType(AnnotationType.Foreground); + break; + case Background: + setAnnotationType(AnnotationType.Background); + break; + case ZoomIn: + zoomIn(); + break; + case ZoomOut: + zoomOut(); + break; + case ZoomOriginal: + zoomOriginal(); + break; + case ZoomBestFit: + zoomBestFit(); + break; + case Repaint: + repaint(); + break; + case Undo: + undo(); + break; + case Redo: + redo(); + break; + case Clear: + clear(); + break; + case SetBrushSize: + showBrushControl(e); + break; + case AutoApply: + setAutoApply(a.action.isChecked()); + break; + case Apply: + apply(); + break; + case SetPainter: + setPainter(); + break; + case SegmenterOptions: + showSegmenterOptions(); + break; + } + + updateToolStates(); + } + + + private void updateToolStates() { + if (isEnabled()) { + Tool.Apply.action.setEnabled(!Tool.AutoApply.action.isChecked()); + Tool.Undo.action.setEnabled(canUndo()); + Tool.Redo.action.setEnabled(canRedo()); + Tool.ZoomIn.action.setEnabled(canZoomIn()); + Tool.ZoomOut.action.setEnabled(canZoomOut()); + Tool.ZoomOriginal.action.setEnabled(canZoomOriginal()); + Tool.ZoomBestFit.action.setEnabled(canZoomBestFit()); + Tool.Foreground.action.setChecked(isAnnotatingForeground()); + Tool.Background.action.setChecked(isAnnotatingBackground()); + Tool.Clear.action.setEnabled(canClear()); + Tool.AutoApply.action.setChecked(auto); + Tool.SegmenterOptions.action.setEnabled(canShowOptions()); + + // Always enabled if view enabled + Tool.SetBrushSize.action.setEnabled(true); + Tool.SegmenterOptions.action.setEnabled(true); + Tool.SetPainter.action.setEnabled(true); + Tool.Repaint.action.setEnabled(true); + Tool.Foreground.action.setEnabled(true); + Tool.Background.action.setEnabled(true); + Tool.AutoApply.action.setEnabled(true); + + } else { + // Everything disabled + for (Tool t : Tool.values()) { + t.action.setEnabled(false); + } + } + + } + + + private void setPainter() { + SegmentationPainter painter = painters.get(combo.getText()); + setPainter(painter); + } + + + private void apply() { + performSegmentation(null, view.getContext()); + } + + + private void showBrushControl(Event e) { + brushControl.showBelow(bar1, (ToolItem) e.widget); + } + + + /** + * Handles a context change in the view. + * + * @param e + * The event. + */ + private void handleContextChanged(ContextChangedEvent e) { + if (e.oldContext != null) { + e.oldContext.removeAnnotationListener(eventHandler); + + // Finish old segmentation + segmenterProxy.finish(e.oldContext); + } + + if (e.newContext != null) { + e.newContext.addAnnotationListener(eventHandler); + + // Begin new segmentation (can cause deferred events + // to be processed, so we async exec to ensure that + // the other context change handlers are called before + // deferred events are processed) + final SegmentationContext ctx = e.newContext; + getDisplay().asyncExec(new Runnable() { + public void run() { + start(ctx); + } + }); + + } + + updateToolStates(); + } + + + /** + * Cleans up resources when the view is disposed. + */ + private void handleDisposed() { + + SegmentationContext ctx = view.getContext(); + if (ctx != null && !ctx.isDisposed()) { + + // Finish any segmentation + segmenterProxy.finish(ctx); + + // Dispose context + ctx.dispose(); + } + + // Dispose all painters + painters.dispose(); + } + + + /** + * Tool bar action. Delegates running to the execute method. + */ + private class ToolAction extends Action { + private final Tool tool; + + public ToolAction(Tool tool) { + super(SegmentationView.this.getText(tool), getType(tool)); + this.tool = tool; + setToolTipText(SegmentationView.this.getToolTip(tool)); + setImageDescriptor(SegmentationView.this.getImage(tool)); + tool.action = this; + } + + public void runWithEvent(Event e) { + execute(tool, e); + } + } + + + /** + * Handles various events coming from the view and toolbar controls. + * + */ + private final class EventHandler extends SelectionAdapter implements + ZoomListener, + SelectionListener, + ContextChangeListener, + AnnotationListener, + DisposeListener + { + + public void widgetDisposed(DisposeEvent e) { + handleDisposed(); + } + + + public void zoomChanged(ZoomEvent e) { + updateToolStates(); + } + + + public void widgetSelected(SelectionEvent e) { + setBrushSize(brushControl.getBrushSize()); + } + + + public void contextChanged(ContextChangedEvent e) { + handleContextChanged(e); + } + + + public void annotationPerformed(AnnotationEvent e) { + if (!isEnabled()) { + log.warning("annotation performed while not enabled"); + } + + if (auto && isEnabled()) { + performSegmentation(e, view.getContext()); + } + updateToolStates(); + } + + + public void annotationRedone(AnnotationEvent e) { + if (!isEnabled()) { + log.warning("annotation redone while not enabled"); + } + + if (auto && isEnabled()) { + performSegmentation(e, view.getContext()); + } + updateToolStates(); + } + + + public void annotationUndone(AnnotationEvent e) { + if (!isEnabled()) { + log.warning("annotation undone while not enabled"); + } + + if (auto && isEnabled()) { + performSegmentation(e, view.getContext()); + } + + updateToolStates(); + } + + + public void annotationsCleared(AnnotationEvent e) { + if (!isEnabled()) { + log.warning("annotations cleared while not enabled"); + } + + if (auto && isEnabled()) { + performSegmentation(e, view.getContext()); + } + updateToolStates(); + } + }; + + /** + * Class that prevents segmentation algorithms crashing the application + * by catching any thrown exceptions and logging them. + * + * @author Kevin McGuinness + */ + private class RobustSegmenterProxy { + + public void init(SegmentationContext ctx) { + if (segmenter != null) { + try { + segmenter.init(ctx); + } catch (Throwable th) { + severe(th, "%s.init()", getSegmenterClassName()); + } + } + } + + public void update(SegmentationContext ctx) { + if (segmenter != null) { + try { + segmenter.update(ctx); + } catch (Throwable th) { + severe(th, "%s.update()", getSegmenterClassName()); + } + } + } + + public void added(SegmentationContext ctx, Annotation a) { + if (segmenter != null) { + try { + segmenter.added(ctx, a); + } catch (Throwable th) { + severe(th, "%s.added()", getSegmenterClassName()); + } + } + } + + public void removed(SegmentationContext ctx, Annotation a) { + if (segmenter != null) { + try { + segmenter.removed(ctx, a); + } catch (Throwable th) { + severe(th, "%s.removed()", getSegmenterClassName()); + } + } + } + + public void finish(SegmentationContext ctx) { + if (segmenter != null) { + try { + segmenter.finish(ctx); + } catch (Throwable th) { + severe(th, "%s.finish()", getSegmenterClassName()); + } + } + } + + public String getSegmenterClassName() { + return segmenter.getClass().getSimpleName(); + } + + private void severe(Throwable th, String message, Object ... args) { + log.log(Level.SEVERE, String.format(message, args), th); + } + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/AnnotatedImageControl.java b/image_annotation/src/ie/dcu/apps/ist/widgets/AnnotatedImageControl.java new file mode 100644 index 0000000..89eb761 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/AnnotatedImageControl.java @@ -0,0 +1,414 @@ +package ie.dcu.apps.ist.widgets; + + +import ie.dcu.apps.ist.controllers.AnnotationTool; +import ie.dcu.apps.ist.event.*; +import ie.dcu.segment.SegmentationContext; +import ie.dcu.segment.annotate.*; +import ie.dcu.segment.painters.*; +import ie.dcu.swt.*; +import ie.dcu.swt.event.ZoomListener; + +import java.util.*; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.*; + +public class AnnotatedImageControl extends Composite { + private final List listeners; + private final ImageControl view; + private SegmentationContext ctx; + private AnnotationTool tool; + private SegmentationPainter painter; + private Cursor cursor; + + public AnnotatedImageControl(Composite parent, int style) { + super(parent, style); + setLayout(new FillLayout()); + view = new ImageControl(this, SWT.NONE); + listeners = new ArrayList(2); + painter = new CombinedPainter(); + view.getCanvas().addMouseMoveListener(cursorChanger); + addDisposeListener(disposeListener); + } + + + public ImageControl getImageControl() { + return view; + } + + + public Canvas getCanvas() { + return view.getCanvas(); + } + + + public SegmentationPainter getPainter() { + return painter; + } + + + public void setPainter(SegmentationPainter painter) { + if (this.painter != painter) { + this.painter = painter; + recreate(); + } + } + + + public SegmentationContext getContext() { + return ctx; + } + + + public void setContext(SegmentationContext ctx) { + + // Remember old context for event + SegmentationContext old = this.ctx; + + // Detach old tool + int lineWidth = 1; + if (tool != null) { + lineWidth = tool.getLineWidth(); + tool.detach(); + tool = null; + } + + // Detach old annotation listener + if (ctx != null) { + ctx.getAnnotations().removeAnnotationListener(listener); + } + + // Assign context + this.ctx = ctx; + + if (ctx != null) { + // Attach new annotation listener + AnnotationManager annotations = ctx.getAnnotations(); + annotations.addAnnotationListener(listener); + + // Attach a new annotation tool + tool = new AnnotationTool(annotations, view); + tool.setLineWidth(lineWidth); + } + + // Create the initial image + recreate(); + + // Fire event + fireContextChanged(old); + } + + + public int getLineWidth() { + if (tool != null) { + return tool.getLineWidth(); + } + return 1; + } + + + public void setLineWidth(int width) { + if (tool != null) { + tool.setLineWidth(width); + } + } + + + public AnnotationType getAnnotationType() { + if (tool != null) { + return tool.getType(); + } + return AnnotationType.Foreground; + } + + + public void setAnnotationType(AnnotationType type) { + if (tool != null) { + tool.setType(type); + } + } + + + public float getZoom() { + return view.getZoom(); + } + + + public void setZoom(float zoom) { + view.setZoom(zoom); + } + + + public float getZoomStep() { + return view.getZoomStep(); + } + + + public void setZoomStep(float zoomStep) { + view.setZoomStep(zoomStep); + } + + + public void zoomBestFit() { + view.zoomBestFit(); + } + + + public void zoomIn() { + view.zoomIn(); + } + + + public void zoomOriginal() { + view.zoomOriginal(); + } + + + public void zoomOut() { + view.zoomOut(); + } + + + public void undo() { + if (ctx != null) { + ctx.getAnnotations().undo(); + } + } + + + public void redo() { + if (ctx != null) { + ctx.getAnnotations().redo(); + } + } + + + public void clear() { + if (ctx != null) { + ctx.getAnnotations().clear(); + } + } + + + public boolean canUndo() { + if (ctx != null) { + return ctx.getAnnotations().canUndo(); + } + return false; + } + + + public boolean canRedo() { + if (ctx != null) { + return ctx.getAnnotations().canRedo(); + } + return false; + } + + + public boolean canClear() { + if (ctx != null) { + return ctx.getAnnotations().count() > 0; + } + return false; + } + + + public boolean canZoomBestFit() { + return view.canZoomBestFit(); + } + + + public boolean canZoomIn() { + return view.canZoomIn(); + } + + + public boolean canZoomOriginal() { + return view.canZoomOriginal(); + } + + + public boolean canZoomOut() { + return view.canZoomOut(); + } + + + /** + * Redraw the entire canvas buffer. + */ + public void repaint() { + if (ctx != null) { + painter.paint(ctx, view.getImage()); + } + } + + + public boolean isAnnotatingForeground() { + return getAnnotationType() == AnnotationType.Foreground; + } + + + public boolean isAnnotatingBackground() { + return getAnnotationType() == AnnotationType.Background; + } + + + public void addAnnotationListener(AnnotationListener listener) { + if (ctx == null) { + throw new IllegalStateException(); + } + ctx.getAnnotations().addAnnotationListener(listener); + } + + + public void removeAnnotationListener(AnnotationListener listener) { + if (ctx != null) { + ctx.getAnnotations().removeAnnotationListener(listener); + } + } + + + public void addZoomListener(ZoomListener listener) { + view.addZoomListener(listener); + } + + + public void removeZoomListener(ZoomListener listener) { + view.removeZoomListener(listener); + } + + + public void addContextChangeListener(ContextChangeListener listener) { + listeners.add(listener); + } + + + public void removeContextChangeListener(ContextChangeListener listener) { + listeners.remove(listener); + } + + + private void fireContextChanged(SegmentationContext old) { + ContextChangedEvent e = null; + for (ContextChangeListener l : listeners) { + if (e == null) { + e = new ContextChangedEvent(this, old, ctx); + } + l.contextChanged(e); + } + } + + + /** + * Re-construct the display image buffer, and dispose the old one if + * necessary. If the context is null, then set the display + * buffer to null and dispose of the old one. + */ + private void recreate() { + if (ctx == null) { + + // Set null image (disposing the old one) + view.setImage(null, true); + + } else { + + // Check if we can reuse what we have + boolean sameSize = false; + if (view.hasImage()) { + if (view.getImageBounds().equals(ctx.getBounds())) { + + // Okay, we can reuse the buffer we have :-) + sameSize = true; + } + } + + if (!sameSize) { + + // Create initial image + ObservableImage buffer = new ObservableImage( + SwtUtils.createImage(ctx.getBounds()) + ); + + // Set the image (disposing the old one) + view.setImage(buffer, true); + } + + // Draw the image + repaint(); + } + } + + + private final DisposeListener disposeListener = new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + if (cursor != null) { + cursor.dispose(); + } + } + }; + + + /** + * Changes the cursor to a cross-hair when mouse is over the image. + * + */ + private final MouseMoveListener cursorChanger = new MouseMoveListener() { + + public void mouseMove(MouseEvent e) { + Canvas canvas = view.getCanvas(); + if (view.imageContains(new Point(e.x, e.y))) { + if (cursor == null) { + cursor = CursorFactory.createCrosshairCursor(); + } + canvas.setCursor(cursor); + } else { + canvas.setCursor(getDisplay().getSystemCursor(SWT.CURSOR_ARROW)); + } + } + }; + + + /** + * Listens for annotations and updates the buffer and view accordingly. + */ + private final AnnotationListener listener = new AnnotationListener() { + + public void annotationUndone(AnnotationEvent e) { + // Suspend notifications to prevent repainting the whole thing + view.getImage().setSuspendNotifications(true); + + // Redraw all to buffer + repaint(); + + // Re-enable notifications + view.getImage().setSuspendNotifications(false); + + // Repaint just the changed area + view.getImage().fireImageChanged(e.annotation.getBounds()); + } + + + public void annotationsCleared(AnnotationEvent e) { + // Repaint everything + repaint(); + } + + + public void annotationRedone(AnnotationEvent e) { + // Paint the new annotation + e.annotation.paint(view.getImage()); + } + + + public void annotationPerformed(AnnotationEvent e) { + // Paint the new annotation + e.annotation.paint(view.getImage()); + } + }; +} diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/BrushControl.java b/image_annotation/src/ie/dcu/apps/ist/widgets/BrushControl.java new file mode 100644 index 0000000..9459f79 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/BrushControl.java @@ -0,0 +1,101 @@ +package ie.dcu.apps.ist.widgets; + +import ie.dcu.swt.PopupComposite; + +import org.eclipse.swt.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +/** + * Pop up control for changing brush sizes. + * + * @author Kevin McGuinness + */ +public class BrushControl extends PopupComposite { + private static final int MAX_BRUSH_SIZE = 20; + + private final Label label; + private final Scale scale; + + + public BrushControl(Shell parent, int style) { + super(parent, style); + + // Create controls + label = new Label(this, SWT.NONE); + scale = new Scale(this, SWT.NONE); + + configureControls(); + configureListeners(); + layoutControls(); + } + + + public void setBrushSize(int size) { + scale.setSelection(size); + updateLabel(); + } + + + public int getBrushSize() { + return scale.getSelection(); + } + + + public void addSelectionListener(SelectionListener listener) { + scale.addSelectionListener(listener); + } + + + public void removeSelectionListener(SelectionListener listener) { + scale.removeSelectionListener(listener); + } + + + private void configureControls() { + scale.setMinimum(1); + scale.setMaximum(MAX_BRUSH_SIZE); + scale.setIncrement(1); + scale.setPageIncrement(2); + setBrushSize(1); + } + + + private void configureListeners() { + scale.addSelectionListener(new ScaleChangeListener()); + } + + + private void layoutControls() { + setLayout(new GridLayout()); + + // Layout label + GridData gd = new GridData(); + gd.grabExcessHorizontalSpace = true; + gd.horizontalAlignment = SWT.FILL; + label.setLayoutData(gd); + + // Layout scale control + gd = new GridData(); + gd.grabExcessHorizontalSpace = true; + gd.horizontalAlignment = SWT.FILL; + gd.minimumWidth = 150; + scale.setLayoutData(gd); + } + + + private void updateLabel() { + int size = scale.getSelection(); + label.setText(String.format("Brush Size: %dpx ", size)); + } + + + private final class ScaleChangeListener + extends SelectionAdapter { + + public void widgetSelected(SelectionEvent e) { + updateLabel(); + } + }; +} diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/ColorSelector.java b/image_annotation/src/ie/dcu/apps/ist/widgets/ColorSelector.java new file mode 100644 index 0000000..7efcf9d --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/ColorSelector.java @@ -0,0 +1,175 @@ +package ie.dcu.apps.ist.widgets; + +import java.beans.*; + +import org.eclipse.jface.resource.*; +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; + +/** + * Button for selecting colors. + * + * @author Kevin McGuinness + */ +public class ColorSelector { + public static final String DATA_KEY = "ColorEditor"; + public static final String COLOR_PROPERTY = "color"; + + private final PropertyChangeSupport pcs; + private final Point extent; + private final Button button; + private final ControlListener listener; + + private Image image; + private RGB rgb; + + + public ColorSelector(Composite parent) { + + pcs = new PropertyChangeSupport(this); + button = new Button(parent, SWT.PUSH); + extent = computeImageSize(parent); + image = new Image(parent.getDisplay(), extent.x, extent.y); + listener = new ControlListener(); + + GC gc = new GC(image); + gc.setBackground(button.getBackground()); + gc.fillRectangle(0, 0, extent.x, extent.y); + gc.dispose(); + + + button.setData(DATA_KEY, this); + button.setImage(image); + button.addListener(SWT.Selection, listener); + button.addListener(SWT.Dispose, listener); + } + + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + + public void addPropertyChangeListener(String name, PropertyChangeListener listener) { + pcs.addPropertyChangeListener(name, listener); + } + + + public PropertyChangeListener[] getPropertyChangeListeners() { + return pcs.getPropertyChangeListeners(); + } + + + public PropertyChangeListener[] getPropertyChangeListeners(String name) { + return pcs.getPropertyChangeListeners(name); + } + + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + + public void removePropertyChangeListener(String name, PropertyChangeListener listener) { + pcs.removePropertyChangeListener(name, listener); + } + + + public RGB getColor() { + return rgb; + } + + + public void setColor(RGB rgb) { + if (rgb != null && this.rgb != rgb) { + RGB old = rgb; + this.rgb = rgb; + updateImage(); + pcs.firePropertyChange(COLOR_PROPERTY, old, rgb); + } + } + + + public Button getButton() { + return button; + } + + + public void addListener(int eventType, Listener listener) { + button.addListener(eventType, listener); + } + + + public void setLayoutData(Object layoutData) { + button.setLayoutData(layoutData); + } + + + public void setData(Object data) { + button.setData(data); + } + + + public Object getData() { + return button.getData(); + } + + + protected void updateImage() { + Display display = button.getDisplay(); + + GC gc = new GC(image); + gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK)); + gc.drawRectangle(0, 2, extent.x - 1, extent.y - 4); + + Color color = new Color(display, rgb); + gc.setBackground(color); + gc.fillRectangle(1, 3, extent.x - 2, extent.y - 5); + gc.dispose(); + + button.setImage(image); + color.dispose(); + } + + + protected Point computeImageSize(Composite parent) { + GC gc = new GC(parent); + Font f = JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT); + gc.setFont(f); + int height = gc.getFontMetrics().getHeight(); + gc.dispose(); + Point p = new Point(height * 3 - 6, height); + return p; + } + + + private void handleDispose(Event e) { + if (image != null) { + image.dispose(); + image = null; + } + } + + + private void handleSelection(Event e) { + ColorDialog dialog = new ColorDialog(button.getShell()); + dialog.setRGB(rgb); + RGB rgb = dialog.open(); + setColor(rgb); + } + + + private final class ControlListener implements Listener { + public void handleEvent(Event e) { + switch (e.type) { + case SWT.Selection: + handleSelection(e); + break; + case SWT.Dispose: + handleDispose(e); + break; + } + } + } +} \ No newline at end of file diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/ImageMenuManager.java b/image_annotation/src/ie/dcu/apps/ist/widgets/ImageMenuManager.java new file mode 100644 index 0000000..cfe0050 --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/ImageMenuManager.java @@ -0,0 +1,54 @@ +package ie.dcu.apps.ist.widgets; + +import java.net.URL; + +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; + +public class ImageMenuManager extends MenuManager { + private Image image; + + public ImageMenuManager() { + super(); + } + + public ImageMenuManager(String text) { + super(text); + } + + public ImageMenuManager(String text, URL imageURL) { + super(text); + setImageURL(imageURL); + } + + public void setImageURL(URL url) { + ImageDescriptor descriptor = ImageDescriptor.createFromURL(url); + this.image = descriptor.createImage(); + } + + @Override + public void dispose() { + super.dispose(); + + if (image != null) { + image.dispose(); + } + } + + @Override + public void fill(Menu parent, int index) { + super.fill(parent, index); + MenuItem item = getMenuItem(); + if (item != null) { + item.setImage(image); + } + } + + protected MenuItem getMenuItem() { + Menu menu = getMenu(); + return (menu != null) ? menu.getParentItem() : null; + } +} diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/SwtTimer.java b/image_annotation/src/ie/dcu/apps/ist/widgets/SwtTimer.java new file mode 100644 index 0000000..6d1f97c --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/SwtTimer.java @@ -0,0 +1,642 @@ +package ie.dcu.apps.ist.widgets; + + +import ie.dcu.apps.ist.event.*; + +import java.util.ArrayList; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.*; + +/** + * A timer user interface element. Displays a component used + * to implement a count-down. + * + * @author Kevin McGuinness + */ +public class SwtTimer implements TickerListener { + + /** + * Timer is modeled as a state machine. These are the states. + */ + public static enum State { Initial, Ready, Running, Paused }; + + + /** + * List of listeners interested in changes to the object state. + */ + private final ArrayList stateListeners; + + + /** + * List of listeners interested in timeouts. + */ + private final ArrayList timeoutListeners; + + + /** + * The drawing canvas where the numbers are drawn. + */ + private final Canvas canvas; + + + /** + * The ticker posts tick events every second or so. + */ + private Ticker ticker; + + + /** + * The current state of the timer. + */ + private State state; + + + /** + * The font used to render the numbers. + */ + private Font font; + + + /** + * Total time for the timer, in seconds. + */ + private int time; + + + /** + * Remaining (displayed) time, in seconds. + */ + private int remaining; + + + /** + * Create the timer. Style bits are passed to the underlying canvas. + * + * @param parent + * Parent container. + * @param style + * Style bits. + */ + public SwtTimer(Composite parent, int style) { + stateListeners = new ArrayList(1); + timeoutListeners = new ArrayList(1); + canvas = new Canvas(parent, style); + addListeners(); + enter(State.Initial); + } + + + /** + * Add listeners to various components. + */ + private void addListeners() { + // Listen for paint events + canvas.addPaintListener(new PaintListener() { + public void paintControl(PaintEvent e) { + paint(e.gc); + } + }); + + // Listen for dispose event + canvas.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + dispose(); + } + }); + } + + + /** + * Set the layout data for the component. + * + * @param data + * The layout data. + */ + public void setLayoutData(Object data) { + canvas.setLayoutData(data); + } + + + /** + * Returns the layout data for the component. + * + * @return The layout data. + */ + public Object getLayoutData() { + return canvas.getLayoutData(); + } + + + /** + * Get the contained canvas. + * + * @return The contained canvas. + */ + public Canvas getCanvas() { + return canvas; + } + + + /** + * Returns the shell associated with the canvas. + * + * @return The shell. + */ + public Shell getShell() { + return canvas.getShell(); + } + + + /** + * Get the state of the timer. + * + * @return The timer state. + */ + public State getState() { + return state; + } + + + /** + * Add a state change listener. + * + * @param listener + * The listener. + */ + public void addStateListener(StateListener listener) { + stateListeners.add(listener); + } + + + /** + * Remove a state change listener. + * + * @param listener + * The listener. + */ + public void removeStateListener(StateListener listener) { + stateListeners.remove(listener); + } + + + /** + * Add a time-out listener. + * + * @param listener + * The listener. + */ + public void addTimeoutListener(TimeoutListener listener) { + timeoutListeners.add(listener); + } + + + /** + * Remove a time-out listener. + * + * @param listener + * The listener. + */ + public void removeTimeoutListener(TimeoutListener listener) { + timeoutListeners.remove(listener); + } + + + /** + * Returns an estimate of the elapsed time, in seconds. + * + * @return The elapsed time. + */ + public int getElapsed() { + return time - remaining; + } + + + /** + * Set the timeout for the timer, in seconds. + * + * @param seconds + * The number of seconds on the timer. + * @throws IllegalStateException + * If {@link #canSet()} is false. + */ + public void set(int seconds) throws IllegalStateException { + switch (state) { + case Initial: + case Ready: + time = seconds; + remaining = time; + break; + default: + throw new IllegalStateException(); + } + enter(State.Ready); + + repaint(); + } + + + /** + * Returns true if set can be called. Set cannot be called when + * the timer is running or paused. + * + * @return true if set can be called. + */ + public boolean canSet() { + switch (state) { + case Initial: + case Ready: + return true; + } + return false; + } + + + /** + * Start the timer. A new thread will control the timer count-down, so this + * method returns once it has started. + * + * @throws IllegalStateException + * If {@link #canStart()} is false. + */ + public void start() throws IllegalStateException { + switch (state) { + case Ready: + ticker = new Ticker(this, 1000); + ticker.start(); + break; + case Paused: + ticker.resume(); + break; + default: + throw new IllegalStateException(); + } + + enter(State.Running); + + repaint(); + } + + + /** + * Returns true if start can be called. Start cannot be called + * when the timer is in it's initial or running state. + * + * @return true if start can be called. + */ + public boolean canStart() { + switch (state) { + case Ready: + case Paused: + return true; + } + return false; + } + + + /** + * Pause the count-down. Temporarily suspends the count-down thread. + * + * @throws IllegalStateException + * If {@link #canPause()} is false. + */ + public void pause() throws IllegalStateException { + switch (state) { + case Running: + ticker.pause(); + break; + case Paused: + break; + default: + throw new IllegalStateException(); + } + + enter(State.Paused); + + repaint(); + } + + + /** + * Returns true if pause can be called. Pause cannot be called + * when the timer is in it's initial or ready state. + * + * @return true if pause can be called. + */ + public boolean canPause() { + switch (state) { + case Running: + case Paused: + return true; + } + return false; + } + + + /** + * Reset the timer. Causes the timer to stop and return to the ready + * state, reseting the time-out to the last one set. + * + * @throws IllegalStateException + * If {@link #canReset()} is false. + */ + public void reset() throws IllegalStateException { + switch (state) { + case Ready: + break; + case Running: + case Paused: + ticker.stop(); + ticker = null; + remaining = time; + break; + default: + throw new IllegalStateException(); + } + + enter(State.Ready); + + repaint(); + } + + + /** + * Returns true if reset can be called. Reset cannot be called + * when the timer is in it's initial state. + * + * @return true if reset can be called. + */ + public boolean canReset() { + switch (state) { + case Ready: + case Running: + case Paused: + return true; + } + return false; + } + + + /** + * Clear the timer. Returns the timer to its initial state, stopping + * it if necessary. + */ + public void clear() { + switch (state) { + case Initial: + break; + case Ready: + time = 0; + remaining = 0; + break; + case Running: + case Paused: + ticker.stop(); + ticker = null; + remaining = 0; + time = 0; + break; + } + + enter(State.Initial); + + repaint(); + } + + + /** + * Returns true if clear can be called. + * + * @return Always returns true. + */ + public boolean canClear() { + return true; + } + + + /** + * Tell the timer to repaint itself. + */ + public void repaint() { + if (!canvas.isDisposed()) { + canvas.redraw(); + } + } + + + /** + * Called at each ticker tick interval. Should not be invoked by clients. + */ + public void tick(final TickerEvent evt) { + if (canvas.isDisposed()) { + // Widget is disposed, ignore timer events + return; + } + + long elapsed = evt.getElapsed(); + int remaining = time - (int) (elapsed / 1000); + + if (remaining <= 0) { + // We're done + this.remaining = 0; + + // Stop and nullify ticker + if (ticker != null) { + ticker.stopLater(); + ticker = null; + } + + // Enqueue the ui update the event dispatch thread + canvas.getDisplay().asyncExec(new Runnable() { + public void run() { + enter(State.Initial); + + // repaint + repaint(); + + // fire timeout event + fireTimeoutEvent(); + } + }); + + } else if (this.remaining != remaining) { + + // Update remaining + this.remaining = remaining; + + // Enqueue a repaint + canvas.getDisplay().asyncExec(new Runnable() { + public void run() { + // repaint + repaint(); + } + }); + } + } + + + /** + * Enter the given state. + * + * @param state The state. + */ + private void enter(State state) { + if (this.state != state) { + this.state = state; + fireStateChanged(); + } + } + + + /** + * Fires a state changed event. + */ + private void fireStateChanged() { + if (!stateListeners.isEmpty()) { + StateEvent evt = new StateEvent(this); + for (StateListener s : stateListeners) { + s.stateChanged(evt); + } + } + } + + + /** + * Sends a timeout event to listeners. + */ + private void fireTimeoutEvent() { + if (!timeoutListeners.isEmpty()) { + TimeoutEvent evt = new TimeoutEvent(this); + for (TimeoutListener t : timeoutListeners) { + t.timeoutOccured(evt); + } + } + } + + + /** + * Paints the timer. + * + * @param gc + * The graphics context. + */ + private void paint(GC gc) { + gc.setFont(getTimerFont()); + + int ss = remaining % 60; + int mm = (remaining % 3600) / 60; + int hh = remaining / 3600; + + String timestr = formatTime(ss, mm, hh); + + Point t = getStringDimensions(gc, timestr); + Point c = getCanvasDimension(); + + int x = c.x / 2 - t.x / 2; + int y = c.y / 2 - t.y / 2; + + if (remaining < 20 && state != State.Initial) { + gc.setForeground(getWarningColor()); + } else { + gc.setForeground(getTimerColor()); + } + + gc.drawString(timestr, x, y); + } + + + /** + * Tidies native resources. Called automatically. + */ + private void dispose() { + if (font != null) { + font.dispose(); + font = null; + } + } + + + /** + * Formats a time string. + */ + private String formatTime(int ss, int mm, int hh) { + String timestr; + if (hh == 0) { + timestr = String.format("%02d:%02d", mm, ss); + } else { + timestr = String.format("%02d:%02d:%02d", hh, mm, ss); + } + return timestr; + } + + + /** + * Returns the dimensions of a given string as drawn with the current font on + * the given graphics context. + */ + private static Point getStringDimensions(GC gc, String str) { + int x = 0; + for (int i = 0; i < str.length(); i++) { + x += gc.getAdvanceWidth(str.charAt(i)); + } + int y = gc.getFontMetrics().getHeight(); + return new Point(x,y); + } + + + /** + * Returns the dimensions of the canvas as a point. + * + * @return A point where x is the width and y is the height. + */ + private Point getCanvasDimension() { + Rectangle bounds = canvas.getBounds(); + return new Point(bounds.width, bounds.height); + } + + + private Color getWarningColor() { + return getSystemColor(SWT.COLOR_RED); + } + + + private Color getTimerColor() { + return getSystemColor(SWT.COLOR_DARK_BLUE); + } + + + private Font getTimerFont() { + if (font == null) { + font = new Font(canvas.getDisplay(), "Sans", 30, SWT.NONE); + } + return font; + } + + + private Color getSystemColor(int id) { + return canvas.getDisplay().getSystemColor(id); + } + + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setBounds(800, 100, 220, 200); + shell.setLayout(new FillLayout()); + + SwtTimer timer = new SwtTimer(shell, SWT.NONE); + timer.set(120); + timer.start(); + + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } + +} diff --git a/image_annotation/src/ie/dcu/apps/ist/widgets/Ticker.java b/image_annotation/src/ie/dcu/apps/ist/widgets/Ticker.java new file mode 100644 index 0000000..c2d48ab --- /dev/null +++ b/image_annotation/src/ie/dcu/apps/ist/widgets/Ticker.java @@ -0,0 +1,246 @@ +package ie.dcu.apps.ist.widgets; + +import ie.dcu.apps.ist.event.*; + + +public class Ticker implements Runnable { + private final TickerListener listener; + + private Thread thread = null; + private boolean pause = false; + private boolean stop = false; + private long napTime = 100; + private long interval = 1000; + private long elapsed = 0; + private long startTime = 0; + private long pausedTime = 0; + + // State variables + private boolean paused = false; + private boolean stopped = true; + + public Ticker(TickerListener listener) { + + // Check listener + if (listener == null) { + throw new IllegalArgumentException(); + } + + this.listener = listener; + } + + + public Ticker(TickerListener listener, long interval) { + + // Check listener + if (listener == null) { + throw new IllegalArgumentException(); + } + + // Check interval + if (interval < 0) { + throw new IllegalArgumentException(); + } + + // Assign + this.listener = listener; + this.interval = interval; + this.napTime = interval / 10; + } + + + public synchronized long elapsed() { + return elapsed; + } + + + public void start() { + if (thread != null) { + throw new IllegalStateException("Cannot restart threads"); + } + thread = new Thread(this, "TimerThread"); + thread.start(); + } + + + public void run() { + + synchronized (this) { + stopped = false; + } + + // Set start time + startTime = System.currentTimeMillis(); + + // Loop + while (true) { + + // Yawn! Z Z z z z Z Z Z z z z Z Z Z ... + sleep(napTime); + + // Wait while thread is paused + synchronized (this) { + if (pause) { + + // Remember when pausing started + long now = System.currentTimeMillis(); + + // Set paused flag to true and notify + paused = true; + notifyAll(); + + // Wait until woken up + while (paused) doWait(); + + // Add duration we've been paused for + pausedTime += System.currentTimeMillis() - now; + + // Clear pause flag && notify + pause = false; + notifyAll(); + } + } + + // Check for stop flag + synchronized (this) { + if (stop) { + stopped = true; + notifyAll(); + break; + } + } + + // HI-HO-HI-HO ... + + work(); + } + } + + + private void work() { + boolean event = false; + + synchronized (this) { + // Calculate total elapsed time in milliseconds + long now = System.currentTimeMillis(); + long elapsed = now - (startTime + pausedTime); + + // Calculate time elapsed since last event + long sinceLast = elapsed - this.elapsed; + if (interval <= sinceLast) { + + // Compensate for any difference + long diff = sinceLast - interval; + + // Its time for another event + this.elapsed = elapsed - diff; + + // Fire event flag + event = true; + } + } + + // Do event + if (event) { + synchronized (listener) { + listener.tick(new TickerEvent(this, elapsed)); + } + } + } + + + public void pause() { + // Pause running thread + synchronized (this) { + pause = true; + + // Wait for pause to occur + while (!paused) doWait(); + } + } + + + public void resume() { + // Wake up paused threads + synchronized (this) { + if (paused) { + paused = false; + notifyAll(); + } + + while (pause) doWait(); + } + } + + + public boolean isPaused() { + synchronized (this) { + return paused; + } + } + + + public void stop() { + + synchronized (this) { + + // Resume paused threads + if (paused) { + paused = false; + notifyAll(); + } + + while (pause) doWait(); + + // Stop + stop = true; + + // Wait until stop block reached + while (!stopped) doWait(); + } + } + + + public void stopLater() { + synchronized (this) { + + // Resume paused threads + if (paused) { + paused = false; + notifyAll(); + } + + while (pause) doWait(); + + // Stop + stop = true; + } + } + + + public boolean isStopped() { + synchronized (this) { + return stopped; + } + } + + + private void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + // Ignore + } + } + + + private void doWait() { + try { + wait(); + } catch (InterruptedException e) { + // ignore + } + } +} + +