Skip to content

Commit

Permalink
Merge pull request #394 from jabooth/memfixes
Browse files Browse the repository at this point in the history
weakref to break cyclic references
  • Loading branch information
jabooth committed Jul 14, 2014
2 parents 0da91be + 16a15cf commit 06f0194
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 47 deletions.
9 changes: 7 additions & 2 deletions menpo/base.py
@@ -1,7 +1,7 @@
import abc
from copy import deepcopy
import os.path
from pathlib import Path
from warnings import warn


class Vectorizable(object):
Expand Down Expand Up @@ -81,7 +81,12 @@ def from_vector(self, vector):
object : ``type(self)``
An new instance of this class.
"""
self_copy = deepcopy(self)
try:
self_copy = self.copy()
except AttributeError:
warn('{}.from_vector() - no copy '
'method, using deepcopy'.format(type(self).__name__))
self_copy = deepcopy(self)
self_copy.from_vector_inplace(vector)
return self_copy

Expand Down
17 changes: 8 additions & 9 deletions menpo/fit/fittingresult.py
@@ -1,6 +1,5 @@
from __future__ import division
import abc
from copy import deepcopy
import numpy as np

from menpo.shape.pointcloud import PointCloud
Expand Down Expand Up @@ -29,7 +28,7 @@ class FittingResult(Viewable):

def __init__(self, image, fitter, gt_shape=None, error_type='me_norm'):
# Initialize the error internal properties
self.image = deepcopy(image)
self.image = image.copy()
self._error_type, self._error_text = None, None
self.fitter = fitter
self.error_type = error_type
Expand Down Expand Up @@ -182,7 +181,7 @@ def view_final_fitting(self, figure_id=None, new_figure=False, **kwargs):
r"""
Displays the final fitting result.
"""
image = deepcopy(self.image)
image = self.image.copy()
image.landmarks['fitting'] = self.final_shape
return image.landmarks['fitting'].view(
figure_id=figure_id, new_figure=new_figure).render(**kwargs)
Expand All @@ -191,7 +190,7 @@ def view_initialization(self, figure_id=None, new_figure=False, **kwargs):
r"""
Displays the initialization from which the fitting started.
"""
image = deepcopy(self.image)
image = self.image.copy()
image.landmarks['fitting'] = self.initial_shape
return image.landmarks['fitting'].view(
figure_id=figure_id, new_figure=new_figure).render(**kwargs)
Expand All @@ -201,7 +200,7 @@ def view_ground_truth(self, figure_id=None, new_figure=False, **kwargs):
Displays the ground truth annotation.
"""
if self.gt_shape is not None:
image = deepcopy(self.image)
image = self.image.copy()
image.landmarks['gt_shape'] = self.gt_shape
return image.landmarks['gt_shape'].view(
figure_id=figure_id, new_figure=new_figure, **kwargs)
Expand Down Expand Up @@ -250,18 +249,18 @@ def n_iters(self):

def shapes(self, as_points=False):
if as_points:
return [deepcopy(s.points) for s in self.parameters]
return [s.points.copy() for s in self.parameters]

else:
return deepcopy(self.parameters)
return [s.copy() for s in self.parameters]

@property
def final_shape(self):
return deepcopy(self.parameters[-1])
return self.parameters[-1].copy()

@property
def initial_shape(self):
return deepcopy(self.parameters[0])
return self.parameters[0].copy()

@FittingResult.gt_shape.setter
def gt_shape(self, value):
Expand Down
5 changes: 5 additions & 0 deletions menpo/fit/test/fittingresult_test.py
Expand Up @@ -6,9 +6,14 @@
from mock import Mock
from menpo.shape import PointCloud
from menpo.testing import is_same_array
from menpo.image import MaskedImage


class MockedFittingResult(FittingResult):

def __init__(self, image, fitter, **kwargs):
FittingResult.__init__(self, MaskedImage.blank((10, 10)), fitter,
**kwargs)
@property
def n_iters(self):
return 1
Expand Down
3 changes: 1 addition & 2 deletions menpo/fitmultilevel/base.py
@@ -1,7 +1,6 @@
from __future__ import division
import abc
import numpy as np
from copy import deepcopy

from menpo.fit.base import Fitter
from menpo.transform import AlignmentAffine, Scale
Expand Down Expand Up @@ -107,7 +106,7 @@ def fit(self, image, initial_shape, max_iters=50, gt_shape=None,
A fitting result object.
"""
# copy image
image = deepcopy(image)
image = image.copy()

# generate image pyramid
images = self._prepare_image(image, initial_shape, gt_shape=gt_shape)
Expand Down
5 changes: 2 additions & 3 deletions menpo/image/base.py
@@ -1,6 +1,5 @@
from __future__ import division
import abc
from copy import deepcopy

import numpy as np
from scipy.misc import imrotate
Expand Down Expand Up @@ -634,7 +633,7 @@ def crop(self, min_indices, max_indices,
Raised if `constrain_to_boundary` is `False`, and an attempt is made
to crop the image in a way that violates the image bounds.
"""
cropped_image = deepcopy(self)
cropped_image = self.copy()
return cropped_image.crop_inplace(
min_indices, max_indices,
constrain_to_boundary=constrain_to_boundary)
Expand Down Expand Up @@ -1208,7 +1207,7 @@ def as_greyscale(self, mode='luminosity', channel=None):
greyscale_image: :class:`MaskedImage`
A copy of this image in greyscale.
"""
greyscale = deepcopy(self)
greyscale = self.copy()
if mode == 'luminosity':
if self.n_dims != 2:
raise ValueError("The 'luminosity' mode only works on 2D RGB"
Expand Down
2 changes: 0 additions & 2 deletions menpo/image/boolean.py
@@ -1,5 +1,3 @@
from copy import deepcopy

import numpy as np

from .base import Image
Expand Down
4 changes: 3 additions & 1 deletion menpo/image/feature/base.py
@@ -1,3 +1,4 @@
from weakref import proxy
import features as fc


Expand All @@ -14,7 +15,8 @@ class ImageFeatures(object):
"""

def __init__(self, image):
self._image = image
# hold a weak ref to the image (to prevent cyclic references)
self._image = proxy(image)

def hog(self, mode='dense', algorithm='dalaltriggs', num_bins=9,
cell_size=8, block_size=2, signed_gradient=True, l2_norm_clip=0.2,
Expand Down
7 changes: 1 addition & 6 deletions menpo/image/masked.py
@@ -1,6 +1,4 @@
from __future__ import division
from copy import deepcopy

import numpy as np
from scipy.ndimage import binary_erosion

Expand Down Expand Up @@ -197,9 +195,6 @@ def copy(self):
Return a new image with copies of the pixels, landmarks, and masks of
this image.
This is an efficient copy method. If you need to copy all the state on
the object, consider deepcopy instead.
Returns
-------
Expand Down Expand Up @@ -593,7 +588,7 @@ def gradient(self, nullify_values_at_mask_boundaries=False):
"""
grad_image_pixels = features.gradient(self.pixels)
grad_image = MaskedImage(grad_image_pixels,
mask=deepcopy(self.mask))
mask=self.mask.copy(), copy=False)

if nullify_values_at_mask_boundaries:
# Erode the edge of the mask in by one pixel
Expand Down
32 changes: 28 additions & 4 deletions menpo/landmark/base.py
@@ -1,4 +1,5 @@
import abc
from weakref import ref

import numpy as np

Expand Down Expand Up @@ -58,8 +59,10 @@ class LandmarkManager(Transformable, Viewable):

def __init__(self, target):
super(LandmarkManager, self).__init__()
self.__target = target
self.__target = None
self._landmark_groups = {}
# use the _target setter last one everything else is cleaned up
self._target = target

def copy(self):
r"""
Expand Down Expand Up @@ -163,13 +166,19 @@ def __delitem__(self, group_label):

@property
def _target(self):
return self.__target
if self.__target is not None:
return self.__target()
else:
return None

@_target.setter
def _target(self, value):
self.__target = value
if value is not None:
self.__target = ref(value)
else:
self.__target = None
for group in self._landmark_groups.itervalues():
group._target = self.__target
group._target = self._target

@property
def n_groups(self):
Expand Down Expand Up @@ -296,6 +305,7 @@ def __init__(self, target, group_label, pointcloud, labels_to_masks,
self._verify_all_labels_masked()

self._group_label = group_label
self.__target = None
self._target = target
if copy:
self._pointcloud = pointcloud.copy()
Expand All @@ -305,6 +315,20 @@ def __init__(self, target, group_label, pointcloud, labels_to_masks,
self._pointcloud = pointcloud
self._labels_to_masks = labels_to_masks

@property
def _target(self):
if self.__target is not None:
return self.__target()
else:
return None

@_target.setter
def _target(self, value):
if value is not None:
self.__target = ref(value)
else:
self.__target = None

def copy(self):
r"""
An efficient copy of this landmark group.
Expand Down
14 changes: 10 additions & 4 deletions menpo/model/modelinstance.py
@@ -1,4 +1,5 @@
from copy import deepcopy
from warnings import warn
import numpy as np

from menpo.base import Targetable, Vectorizable, DP
Expand Down Expand Up @@ -297,10 +298,15 @@ def __init__(self, model, global_transform_cls):
# 1. Construct similarity model from the mean of the model
self.similarity_model = Similarity2dInstanceModel(model.mean)
# 2. Orthonormalize model and similarity model
model = deepcopy(model)
model.orthonormalize_against_inplace(self.similarity_model)
self.similarity_weights = self.similarity_model.project(model.mean)
super(OrthoPDM, self).__init__(model, global_transform_cls)
try:
model_cpy = model.copy()
except AttributeError:
warn('OrthoPDM() - no copy on model {}'
', using deepcopy'.format(type(model).__name__))
model_cpy = deepcopy(model)
model_cpy.orthonormalize_against_inplace(self.similarity_model)
self.similarity_weights = self.similarity_model.project(model_cpy.mean)
super(OrthoPDM, self).__init__(model_cpy, global_transform_cls)

@property
def global_parameters(self):
Expand Down
15 changes: 13 additions & 2 deletions menpo/transform/base/__init__.py
@@ -1,5 +1,4 @@
import abc
from copy import deepcopy


class Transform(object):
Expand Down Expand Up @@ -242,6 +241,18 @@ def _transform_inplace(self, transform):
The transformed object, having been transformed in place.
"""

@abc.abstractmethod
def copy(self):
r"""
Return an efficient copy of self.
Returns
-------
copy : ``type(self)``
An efficient copy of self.
"""

def _transform(self, transform):
r"""
Apply the :map:`Transform` given in a non destructive manner -
Expand All @@ -257,7 +268,7 @@ def _transform(self, transform):
transformed : ``type(self)``
A copy of the object, transformed.
"""
copy_of_self = deepcopy(self)
copy_of_self = self.copy()
# transform the copy destructively
copy_of_self._transform_inplace(transform)
return copy_of_self
Expand Down

0 comments on commit 06f0194

Please sign in to comment.