Skip to content

Commit

Permalink
Merge Merge 2.10 on to release branch (#97)
Browse files Browse the repository at this point in the history
Merge 2.10 on to release branch
  • Loading branch information
ssilburn authored Nov 28, 2022
2 parents 9b0ceac + fc14015 commit a05cbd7
Show file tree
Hide file tree
Showing 62 changed files with 2,813 additions and 5,266 deletions.
28 changes: 27 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
Calcam Changelog
================

Minor Release 2.10.0 (November 2022)
------------------------------------

New & Enhancements:
* Improved handling of errors which prevent the GUI from starting, so the user should always at least get an error message instead of silent failure.
* Added missing documentation for calcam.movement.phase_correlation_movement() so this is now part of the public API.
* Added WSAD & Up/Down/Left/Right keyboard controls for "walking" in the 3D views (#93)
* Switched to using vtkStaticCellLocator() for ray-casting because it's much faster but also for some reason much more accurate with newer versions of VTK.
* Minor documentation improvements

Compatibility:
* Removed support for PyQt6, because it causes more problems than it's worth on most platforms so sticking with PyQt5 for now gives an easier / more reliable user experience.
* Fixed various problems with the GUI under Python 3.10:
* Fixed an issue which prevented the main GUI tools from opening at alll (silent failure) (#94)
* Fixed non-working colour chooser (#95)
* Fix exceptions related to sliders (part of #78)
* Fixed exception raised when rendering unfolded wall in Viewer tool
* Added workaround / mitigation for silent Python crashes with VTK => 9.1 when setting some CAD models to wireframe (#78)
* Removed restriction of VTK version < 9.1 in setup.py and updated docs accordingly.

Fixes:
* Fixed an exception raised when adding CAD viewports from a calibration file in the Viewer GUI if the user had not manually resized the window since it was opened. (#96)
* Fixed an exception raised by Calibration.project_points() for fisheye lens calibrations if it was called with only a single 3D point to project.
* Fixed a bug where calling get_model_normals() on a raycast result with no model normals would return an exception class, instead of raising an exception.
* Fixed a couple of left-over legacy variable names in the CADModel class


Patch Release 2.9.1 (23rd July 2022)
------------------------------------
Expand All @@ -20,7 +46,7 @@ New & Enhancements:
* Calcam now available on PyPi (but could use improvements to make distribution more PEP compliant in future) (#83)
* Support masking of images (i.e marking parts of the image which do not contain any actual image) in fitting and manual alignment calibration tools (#46).
* Add vertical field of view input in virtual calibration editor as an alternative to focal length (#85).
* Remove error message if fitting determines the camera is upside-down; camera roll not settable in range -180,180 instead of -90,90 degrees.
* Remove error message if fitting determines the camera is upside-down; camera roll now settable in range -180,180 instead of -90,90 degrees.
* In the virtual calibration editor, hide pinhole model parameters if another intrinsics type is selected (otherwise could be misleading).
* In the virtual calibration editor, when using intrinsics from an existing calibration, display the filename of that calibration.
* Add button to save image to use as a template for creating image masks.
Expand Down
13 changes: 12 additions & 1 deletion calcam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
CalCam package.
"""
import os
import sys
import warnings

# Calcam version
Expand Down Expand Up @@ -65,4 +66,14 @@

# If we have no GUI available, put a placeholder function to print a message about why where the GUI launcher would normally be.
if no_gui_reason is not None:
start_gui = lambda : print('Could not start calcam GUI: {:s}'.format(no_gui_reason))

start_gui = lambda: print('Could not start calcam GUI: {:s}'.format(no_gui_reason))

# If we're running under pythonw, that print statement won't do anything, so try to make a simple tkinter dialog box to tell the user about the error.
if 'pythonw' in os.path.split(sys.executable)[-1]:
try:
from tkinter import messagebox
start_gui = lambda: messagebox.showerror(title='Cannot start Calcam GUI',message='Cannot start Calcam GUI: {:s}'.format(no_gui_reason))
except Exception as e:
pass

2 changes: 1 addition & 1 deletion calcam/__version__
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.9.1
2.10.0
11 changes: 8 additions & 3 deletions calcam/cadmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def __init__(self,model_name=None,model_variant=None,status_callback=print_statu
self.def_file.close()
raise

if type(test_out) == str or type(test_out) == unicode:
if type(test_out) in [str,bytes]:
self.usermodule = usermodule
else:
self.def_file.close()
Expand Down Expand Up @@ -444,7 +444,7 @@ def reset_colour(self,features=None):
elif feature in self.features.keys():
self.features[feature].set_colour(self.features[feature].default_colour)
else:
raise ValueError('Unknown feature "{:s}"!'.format(requested))
raise ValueError('Unknown feature "{:s}"!'.format(feature))



Expand Down Expand Up @@ -596,7 +596,7 @@ def get_cell_locator(self):

appender.Update()

self.cell_locator = vtk.vtkCellLocator()
self.cell_locator = vtk.vtkStaticCellLocator()
self.cell_locator.SetTolerance(1e-6)
self.cell_locator.SetDataSet(appender.GetOutput())
self.cell_locator.BuildLocator()
Expand Down Expand Up @@ -854,6 +854,11 @@ def get_polydata(self):

self.polydata = scaler.GetOutput()

# Remove all the lines from the PolyData. As far as I can tell for "normal" mesh files this shouldn't
# remove anything visually important, but it avoids running in to issues with vtkFeatureEdges trying to allocate
# way too much memory in VTK 9.1+.
self.polydata.SetLines(vtk.vtkCellArray())

if self.parent.status_callback is not None:
self.parent.status_callback(None)

Expand Down
7 changes: 4 additions & 3 deletions calcam/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,11 @@ def normalise(self,x,y):
def project_points(self,points,include_distortion=True):

# Check the input points are in a suitable format
if np.ndim(points) < 3:
points = np.array([points],dtype='float32')
else:
if np.ndim(points) == 3:
points = np.array(points,dtype='float32')
else:
while np.ndim(points) < 3:
points = np.array([points], dtype='float32')

if include_distortion:
kc = self.kc
Expand Down
2 changes: 1 addition & 1 deletion calcam/gui/cad_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def __init__(self, app, parent = None,filename=None):
self.rename_variant_button.clicked.connect(self.rename_variant)
self.model_variant.currentIndexChanged.connect(self.change_variant)

self.control_sensitivity_slider.setValue(self.config.mouse_sensitivity)
self.control_sensitivity_slider.setValue(int(self.config.mouse_sensitivity))
self.remove_variant_button.clicked.connect(self.remove_variant)

self.feature_tree.itemChanged.connect(self.on_featuretree_change)
Expand Down
11 changes: 7 additions & 4 deletions calcam/gui/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def init(self, ui_filename, app, parent):

self.app = app

# Let's show helpful dialog boxes if we have unhandled exceptions:
sys.excepthook = self.show_exception_dialog

self.config = CalcamConfig()

self.manual_exc = False
Expand All @@ -66,12 +69,10 @@ def init(self, ui_filename, app, parent):
available_space = self.app.primaryScreen().availableGeometry()

# Open the window with same aspect ratio as the screen, and no fewer than 500px tall.
win_height = max(500,min(780,0.75*available_space.height()))
win_width = win_height * available_space.width() / available_space.height()
win_height = int(max(500,min(780,0.75*available_space.height())))
win_width = int(win_height * available_space.width() / available_space.height())
self.resize(win_width,win_height)

# Let's show helpful dialog boxes if we have unhandled exceptions:
sys.excepthook = self.show_exception_dialog

try:
self.action_new.setIcon( qt.QIcon(os.path.join(guipath,'icons','new.png')) )
Expand Down Expand Up @@ -321,6 +322,8 @@ def pick_colour(self,init_colour,pick_alpha=False):

col_init = np.array(init_colour) * 255

col_init = np.round(col_init).astype(int)

if pick_alpha:
if col_init.size < 4:
raise ValueError('If pick_alpha = True, you must supply a 4 element RGBA initial colour!')
Expand Down
2 changes: 1 addition & 1 deletion calcam/gui/fitting_calib.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def __init__(self, app, parent = None, load_file=None):

self.fit_initted = False

self.overlay_opacity_slider.setValue(self.config.main_overlay_colour[3]*100)
self.overlay_opacity_slider.setValue(int(self.config.main_overlay_colour[3]*100))
self.overlay_appearance_controls.hide()
self.comparison_overlay_appearance.hide()

Expand Down
57 changes: 30 additions & 27 deletions calcam/gui/qt_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"""

# Import all the Qt bits and pieces from the relevant module

''' PyQt6 support removed because it works very poorly with VTK, and PyQt5 is still readily available.
try:
from PyQt6.QtCore import *
from PyQt6.QtGui import *
Expand All @@ -42,36 +44,37 @@
pyqt6_broken=True
except Exception:
pyqt6_broken=False
'''

try:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QTreeWidgetItem as QTreeWidgetItem_class
from PyQt5 import uic
qt_ver = 5
try:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QTreeWidgetItem as QTreeWidgetItem_class
from PyQt5 import uic
qt_ver = 5

except Exception:
try:
import PyQt5
pyqt5_broken=True
except:
pyqt5_broken=False
try:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtGui import QTreeWidgetItem as QTreeWidgetItem_class
from PyQt4 import uic
qt_ver = 4
except Exception:
try:
import PyQt5
pyqt5_broken=True
except:
pyqt5_broken=False
try:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtGui import QTreeWidgetItem as QTreeWidgetItem_class
from PyQt4 import uic
qt_ver = 4
except Exception:

if pyqt6_broken:
raise ImportError('Could not import required GUI library: Python package "PyQt6" is present but seems to be broken.')

if pyqt5_broken:
raise ImportError('Could not import required GUI library: Python package "PyQt5" is present but seems to be broken.')

raise ImportError('Could not import required GUI library: could not import either "PyQt6", "PyQt5" or "PyQt4" python packages successfully.')

#if pyqt6_broken:
# raise ImportError('Could not import required GUI library: Python package "PyQt6" is present but seems to be broken.')

if pyqt5_broken:
raise ImportError('Could not import required GUI library: Python package "PyQt5" is present but seems to be broken.')

raise ImportError('Could not import required GUI library: could not import either "PyQt6", "PyQt5" or "PyQt4" python packages successfully.')


if qt_ver == 6:
Expand Down
3 changes: 1 addition & 2 deletions calcam/gui/qvtkrenderwindowinteractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,7 @@ def keyPressEvent(self, ev):
if keySym is not None and shift and len(keySym) == 1 and keySym.isalpha():
keySym = keySym.upper()

self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
ctrl, shift, key, 0, keySym)
self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,ctrl, shift, key[-1], 0, keySym)
self._Iren.KeyPressEvent()
self._Iren.CharEvent()

Expand Down
8 changes: 4 additions & 4 deletions calcam/gui/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def __init__(self, app, parent = None):
self.remove_lines_button.clicked.connect(self.update_lines)
self.coords_legend_checkbox.toggled.connect(self.update_legend)

self.control_sensitivity_slider.setValue(self.config.mouse_sensitivity)
self.control_sensitivity_slider.setValue(int(self.config.mouse_sensitivity))

self.proj_perspective.toggled.connect(self.set_projection)

Expand Down Expand Up @@ -343,7 +343,7 @@ def set_fov_opacity(self,opacity,actor_type):
opacity = opacity**(1/gamma)

self.sightline_opacity_slider.blockSignals(True)
self.sightline_opacity_slider.setValue(opacity * 100)
self.sightline_opacity_slider.setValue(int(opacity * 100))
self.sightline_opacity_slider.blockSignals(False)


Expand Down Expand Up @@ -889,7 +889,7 @@ def update_sightlines(self,data):
self.statusbar.showMessage('Ray casting camera sight lines...')
self.app.processEvents()
if self.sightlines[data][2] == 'wall_coverage':
actor = render.get_wall_coverage_actor(self.sightlines[data][0],self.cadmodel,resolution=256,subview=self.sightlines[data][3])
actor = render.get_wall_coverage_actor(self.sightlines[data][0],self.cadmodel,subview=self.sightlines[data][3],clearance=1e-2,resolution=256)
else:
actor = render.get_fov_actor(self.cadmodel,self.sightlines[data][0],self.sightlines[data][2],subview=self.sightlines[data][3])
self.statusbar.clearMessage()
Expand Down Expand Up @@ -1061,7 +1061,7 @@ def update(self,progress):
self.parent.app.processEvents()
self.repaint()
try:
self.progressbar.setValue(progress*100)
self.progressbar.setValue(int(progress*100))
if self.progress_ref is None:
self.progress_ref = (time.time(),progress)
else:
Expand Down
Loading

0 comments on commit a05cbd7

Please sign in to comment.