Skip to content

Commit

Permalink
Merge pull request #290 from NaturalHistoryMuseum/feature/212-box-order
Browse files Browse the repository at this point in the history
[#212] loads of work on box sorting
  • Loading branch information
quicklizard99 committed Apr 28, 2016
2 parents c24414f + 12ccb3d commit 20fcf55
Show file tree
Hide file tree
Showing 26 changed files with 413 additions and 176 deletions.
23 changes: 20 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,28 @@ virtualenv:

before_install:
- sudo apt-get update

install:
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then
sudo apt-get install --fix-missing python-opencv python-numpy python-pyside libtiff4-dev libjpeg8-dev zlib1g-dev libzbar-dev;
sudo apt-get install -y --fix-missing libzbar-dev;
fi

# We do this conditionally because it saves us some downloading if the
# version is the same.
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh;
fi
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda update --yes conda
# Useful for debugging any issues with conda
- conda info -a

- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION pillow pyside numpy scikit-learn opencv
- source activate test-environment
- python setup.py install

install:
- pip install -r requirements.txt
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ This is an overview of major changes. Refer to the git repository for a full log

Version 0.1.28
-------------
- Fixed #291 - Alter travis to use Anaconda / Miniconda
- Fixed #288 - Minimap navigator
- Fixed #286 - Disable default template command when the default template selected
- Fixed #284 - Document info view to contain links
- Fixed #281 - Don't make images read-only
- Fixed #212 - Customized bounding box ordering
- Fixed #204 - Improvements to subsegment dots
- Fixed #203 - Resize handles should have constant size regardless of zoom

Expand Down
15 changes: 10 additions & 5 deletions DevelopingOnMacOSX.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,24 @@ cd ~/projects/inselect
pip install -r requirements.txt
```

# OpenCV
`numpy` is pinned by opencv installation
## OpenCV
Version of `numpy` is pinned by opencv installation but we want latest version -
I have not encountered any problems by installing the latest version.

`jjhelmus` provides versions after `2.4.10` but these make the Mac build
extremely problematic by introducing many `dylib` dependencies that are
troublesome to freeze.
```
conda install --yes -c https://conda.binstar.org/jjhelmus opencv
conda install --yes -c https://conda.binstar.org/jjhelmus opencv=2.4.10
conda install --yes numpy
```

# setuptools
## setuptools
A [bug in PyInstaller 3.1.1](https://github.com/pyinstaller/pyinstaller/issues/1773)
means that we need to use setupools 19.2:

```
conda install setuptools=19.2
conda install --yes setuptools=19.2
```

## LibDMTX barcode reading library
Expand Down
4 changes: 2 additions & 2 deletions DevelopingOnWindows.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ follows:

```
conda update --yes conda
conda create --yes --name inselect pillow pyside pywin32 numpy
conda create --yes --name inselect pillow pyside pywin32 numpy scikit-learn
activate inselect
python -m pip install --upgrade pip
pip install -r requirements.txt
Expand Down Expand Up @@ -92,7 +92,7 @@ follows:

```
conda update --yes conda
conda create --yes --name inselect pillow pyside pywin32 numpy
conda create --yes --name inselect pillow pyside pywin32 numpy scikit-learn
activate inselect
python -m pip install --upgrade pip
pip install -r requirements.txt
Expand Down
8 changes: 6 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
rm -rf build dist
pyinstaller --clean inselect.spec

for script in export_metadata ingest read_barcodes save_crops segment; do
for script in export_metadata ingest read_barcodes save_crops; do
rm -rf $script.spec
pyinstaller --onefile --icon=data/inselect.icns inselect/scripts/$script.py
pyinstaller --onefile --hidden-import numpy inselect/scripts/$script.py
done
# segment has an additional hidden import
rm -rf segment.spec
pyinstaller --onefile --hidden-import numpy \
--hidden-import sklearn.neighbors.typedefs inselect/scripts/segment.py

# Add a few items to the PropertyList file generated by PyInstaller
python -m bin.plist dist/inselect.app/Contents/Info.plist
Expand Down
27 changes: 15 additions & 12 deletions inselect.spec
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# For PyInstaller build on Mac

import sys

from pathlib import Path

block_cipher = None
Expand All @@ -9,7 +10,7 @@ a = Analysis(['inselect.py'],
pathex=[str(Path('.').absolute())],
binaries=None,
datas=None,
hiddenimports=[],
hiddenimports=['sklearn.neighbors.typedefs'],
hookspath=[],
runtime_hooks=[],
excludes=['Tkinter'],
Expand All @@ -18,23 +19,25 @@ a = Analysis(['inselect.py'],
cipher=block_cipher)


# PyInstaller does not detect some dylibs, I think because they are symlinked.
# PyInstaller does not detect some dylibs, I think in some cases because they
# are symlinked.
# See Stack Overflow post http://stackoverflow.com/a/17595149 for example
# of manipulating Analysis.binaries.

# Tuples (name, source)
MISSING_DYLIBS = (
('libQtCore.4.dylib', 'libQtCore.4.8.7.dylib'),
('libQtGui.4.dylib', 'libQtGui.4.8.7.dylib'),
('libpng16.16.dylib', 'libpng16.16.dylib'),
('libz.1.dylib', 'libz.1.dylib'),
'libQtCore.4.dylib',
'libQtGui.4.dylib',
'libpng16.16.dylib',
'libz.1.dylib',
)

# The lib directory associated with this environment
LIB = Path(sys.argv[0]).parent.parent.joinpath('lib')

a.binaries += TOC([(name, str(LIB.joinpath(source)), 'BINARY') for name, source in MISSING_DYLIBS])

# Find the source for each library and add it to the list of binaries
a.binaries += TOC([
(lib, str(LIB.joinpath(lib).resolve()), 'BINARY') for lib in MISSING_DYLIBS
])
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

# Prefer to freeze to a folder rather than a single file. The choice makes no
Expand All @@ -51,7 +54,7 @@ if SINGLE_FILE:
name='inselect',
debug=False,
strip=False,
upx=True,
upx=False,
console=False,
icon='data/inselect.icns')
else:
Expand All @@ -62,7 +65,7 @@ else:
name='inselect',
debug=False,
strip=False,
upx=True,
upx=False,
console=False,
icon='data/inselect.icns')

Expand All @@ -71,7 +74,7 @@ else:
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx=False,
name='inselect')


Expand Down
47 changes: 39 additions & 8 deletions inselect/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from .plugins.subsegment import SubsegmentPlugin
from .recent_documents import RecentDocuments
from .roles import RotationRole
from .sort_document_items import sort_items_choice
from .user_template_choice import user_template_choice
from .utils import contiguous, report_to_user, qimage_of_bgr
from .views.boxes import BoxesView, GraphicsItemView
Expand Down Expand Up @@ -963,11 +964,11 @@ def _create_menu_actions(self):
triggered=partial(self.select_next_prev, next=False)
)
self.select_by_size_larger_action = QAction(
"Select increasing size", self, shortcut="ctrl+>",
"Select &increasing size", self, shortcut="ctrl+>",
triggered=partial(self.select_by_size_step, larger=True)
)
self.select_by_size_smaller_action = QAction(
"Select decreasing size", self, shortcut="ctrl+<",
"Select d&ecreasing size", self, shortcut="ctrl+<",
triggered=partial(self.select_by_size_step, larger=False)
)

Expand All @@ -984,14 +985,23 @@ def _create_menu_actions(self):
self.delete_action.shortcut()])

self.rotate_clockwise_action = QAction(
"Rotate clockwise", self, shortcut="ctrl+R",
"Rotate c&lockwise", self, shortcut="ctrl+R",
triggered=partial(self.rotate90, clockwise=True)
)
self.rotate_counter_clockwise_action = QAction(
"Rotate counter-clockwise", self, shortcut="ctrl+L",
"Rotate c&ounter-clockwise", self, shortcut="ctrl+L",
triggered=partial(self.rotate90, clockwise=False)
)

self.sort_by_rows_action = QAction(
"Sort by &rows", self, checkable=True,
triggered=partial(self.sort_boxes, by_columns=False)
)
self.sort_by_columns_action = QAction(
"Sort by &columns", self, checkable=True,
triggered=partial(self.sort_boxes, by_columns=True)
)

# Plugins
# Plugin shortcuts start at F5
shortcut_offset = 5
Expand Down Expand Up @@ -1054,7 +1064,7 @@ def _create_menu_actions(self):
icon=self.style().standardIcon(QtGui.QStyle.SP_ArrowDown)
)
self.zoom_home_action = QAction(
"Whole image", self,
"&Whole image", self,
shortcut=QtGui.QKeySequence.MoveToStartOfDocument,
triggered=self.zoom_home, checkable=True
)
Expand All @@ -1071,10 +1081,10 @@ def _create_menu_actions(self):
)

self.show_object_grid_action = QAction(
'Show grid', self, shortcut='ctrl+G', triggered=self.show_grid
'Show &grid', self, shortcut='ctrl+G', triggered=self.show_grid
)
self.show_object_expanded_action = QAction(
'Show expanded', self,
'Show &expanded', self,
shortcut='ctrl+E', triggered=self.show_expanded
)

Expand Down Expand Up @@ -1184,6 +1194,9 @@ def _create_menus(self):
self._edit_menu.addAction(self.rotate_clockwise_action)
self._edit_menu.addAction(self.rotate_counter_clockwise_action)
self._edit_menu.addSeparator()
self._edit_menu.addAction(self.sort_by_rows_action)
self._edit_menu.addAction(self.sort_by_columns_action)
self._edit_menu.addSeparator()
user_template_popup = self._edit_menu.addMenu('Metadata template')
self.view_metadata.popup_button.inject_actions(user_template_popup)
self._edit_menu.addSeparator()
Expand All @@ -1210,7 +1223,7 @@ def _create_menus(self):
self._view_menu.addAction(self.show_object_grid_action)
self._view_menu.addAction(self.show_object_expanded_action)
self._view_menu.addSeparator()
colours_popup = self._view_menu.addMenu('Colour scheme')
colours_popup = self._view_menu.addMenu('&Colour scheme')
for action in self.colour_scheme_actions:
colours_popup.addAction(action)

Expand Down Expand Up @@ -1345,6 +1358,22 @@ def copy_to_new_document(self):
else:
self.new_document(path, default_metadata_items=items)

@report_to_user
def sort_boxes(self, by_columns):
"""Sorts boxes either by columns or by rows.
"""
if self.document:
# Sort boxes
self.model.to_document(self.document)
items = sort_items_choice().sort_items(
self.document.items, by_columns
)
self.model.set_new_boxes(items)
else:
# Record the user's choice
sort_items_choice().sort_items([], by_columns)
self.sync_ui()

def _accept_drag_drop(self, event):
"""If event refers to a single file that can opened, returns the path.
Returns None otherwise.
Expand Down Expand Up @@ -1428,6 +1457,8 @@ def sync_ui(self):
self.delete_action.setEnabled(has_selection)
self.rotate_clockwise_action.setEnabled(has_selection)
self.rotate_counter_clockwise_action.setEnabled(has_selection)
self.sort_by_rows_action.setChecked(not sort_items_choice().by_columns)
self.sort_by_columns_action.setChecked(sort_items_choice().by_columns)
self.cookie_cutter_widget.sync_actions(document, has_rows)
for action in self.plugin_actions:
action.setEnabled(document)
Expand Down
13 changes: 10 additions & 3 deletions inselect/gui/plugins/segment.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from PySide.QtGui import QIcon, QMessageBox

from inselect.lib.segment import segment_document
from inselect.lib.segment_document import SegmentDocument
from inselect.lib.utils import debug_print

from inselect.gui.sort_document_items import sort_items_choice

from .plugin import Plugin


Expand All @@ -16,6 +18,7 @@ def __init__(self, document, parent):
self.rects = self.display = None
self.document = document
self.parent = parent
self.sort_choice = sort_items_choice().by_columns

@classmethod
def icon(cls):
Expand All @@ -34,8 +37,12 @@ def can_be_run(self):

def __call__(self, progress):
debug_print('SegmentPlugin.__call__')
doc, display = segment_document(self.document, callback=progress)
doc, display = SegmentDocument(self.sort_choice).segment(
self.document, callback=progress
)

self.items, self.display = doc.items, display

debug_print('SegmentPlugin.__call__ exiting. Found [{0}] boxes'.format(len(self.items)))
debug_print('SegmentPlugin.__call__ exiting. Found [{0}] boxes'.format(
len(self.items))
)
Loading

0 comments on commit 20fcf55

Please sign in to comment.