Repository: scikit-image/scikit-image

Issue:
Regionprops list index method
Hello. I've been experiencing some problems while iterating through a list and calling the method index to get the corresponding index.

The code:

```
from skimage.morphology import label
from skimage.measure import regionprops

image = imread(path ,1)

#apply threshold in order to make the image binary
img = image > 120

# label image regions
label_img = label(img, neighbors=8, background=1) + 1 #fix problem               
letters = regionprops(label_img)

for l in letters:
        i = letters.index(l)
```

The error is the following:

```
    i = letters.index(l)
  File "/usr/lib/python2.7/_abcoll.py", line 407, in __eq__
    return dict(self.items()) == dict(other.items())
  File "/usr/lib/python2.7/_abcoll.py", line 395, in items
    return [(key, self[key]) for key in self]
  File "/usr/lib/python2.7/site-packages/skimage/measure/_regionprops.py", line 328, in __getitem__
    return getattr(self, PROPS[key])
KeyError: 'min_intensity'
```

I guess the error is caused because it can't identify the 'min_intensity' propriety, what is strange, since it has it's cached method declarated in the _regionprops.py.



Discussion:
Hi Eduardo,

I don't quite understand the error, but it probably has something to do
with needing to define an equality operator (**eq**) in a RegionProps
object. In the meantime though, your code is a hopelessly inefficient way
to do this. Instead, do:

for i, l in enumerate(letters):

Hope that helps!

Juan.

On Thu, Mar 20, 2014 at 10:34 AM, Eduardo Henrique Arnold <
notifications@github.com> wrote:

> Hello. I've been experiencing some problems while iterating through a list
> and calling the method index to get the corresponding index.
> 
> The code:
> 
> from skimage.morphology import label
> from skimage.measure import regionprops
> 
> image = imread(path ,1)
> 
> #apply threshold in order to make the image binary
> img = image > 120
> 
> # label image regions
> 
> label_img = label(img, neighbors=8, background=1) + 1 #fix problem
> letters = regionprops(label_img)
> 
> for l in letters:
>         i = letters.index(l)
> 
> The error is the following:
> 
> ```
> i = letters.index(l)
> ```
> 
>   File "/usr/lib/python2.7/_abcoll.py", line 407, in __eq__
>     return dict(self.items()) == dict(other.items())
>   File "/usr/lib/python2.7/_abcoll.py", line 395, in items
>     return [(key, self[key]) for key in self]
>   File "/usr/lib/python2.7/site-packages/skimage/measure/_regionprops.py", line 328, in __getitem__
>     return getattr(self, PROPS[key])
> KeyError: 'min_intensity'
> 
> I guess the error is caused because it can't identify the 'min_intensity'
> propriety, what is strange, since it has it's cached method declarated in
> the _regionprops.py.
> 
> ## 
> 
> Reply to this email directly or view it on GitHubhttps://github.com/scikit-image/scikit-image/issues/945
> .

Hello, jni, thank you for the quick answer!

Yes, my solution was in fact the enumerate in order to iterate through the list, but I was only debugging the code and needed to get the index of the object without much effort, so I found this error. 

I thought that maybe this error may interfere with other functionality so it may worth to fix it. 

I appreciate your answer, thank you!

No worries!

@ahojnnes, do you have any idea what's going on here? 

The problem is that `intensity_image` is `None`
Therefore `region['min_intensity']` is `None` ( `np.min(None) = None` )
Now the code assumes a deprecated property and that's where the error is raised.

We can fix this by overloading the `__equals__` method. 

Boom. Nice hunting, @vighneshbirodkar!

Does it make sense to define the equality of two regionprops ?

I think it does! Two regionprops are equal iff all the individual properties are equal (including "None").


Resolving patch:
diff --git a/skimage/measure/_regionprops.py b/skimage/measure/_regionprops.py
--- a/skimage/measure/_regionprops.py
+++ b/skimage/measure/_regionprops.py
@@ -56,6 +56,8 @@
     'WeightedNormalizedMoments': 'weighted_moments_normalized'
 }
 
+PROP_VALS = PROPS.values()
+
 
 class _cached_property(object):
     """Decorator to use a function as a cached property.
@@ -327,6 +329,20 @@ def __getitem__(self, key):
                           category=DeprecationWarning)
             return getattr(self, PROPS[key])
 
+    def __eq__(self, other):
+        if not isinstance(other, _RegionProperties):
+            return False
+
+        for key in PROP_VALS:
+            try:
+                #so that NaNs are equal
+                np.testing.assert_equal(getattr(self, key, None),
+                                        getattr(other, key, None))
+            except AssertionError:
+                return False
+
+        return True
+
 
 def regionprops(label_image, properties=None,
                 intensity_image=None, cache=True):


Test patch:
diff --git a/skimage/measure/tests/test_regionprops.py b/skimage/measure/tests/test_regionprops.py
--- a/skimage/measure/tests/test_regionprops.py
+++ b/skimage/measure/tests/test_regionprops.py
@@ -371,6 +371,22 @@ def get_intensity_image():
     assert_raises(AttributeError, get_intensity_image)
 
 
+def test_equals():
+    arr = np.zeros((100, 100), dtype=np.int)
+    arr[0:25, 0:25] = 1
+    arr[50:99, 50:99] = 2
+
+    regions = regionprops(arr)
+    r1 = regions[0]
+
+    regions = regionprops(arr)
+    r2 = regions[0]
+    r3 = regions[1]
+
+    assert_equal(r1 == r2, True, "Same regionprops are not equal")
+    assert_equal(r1 != r3, True, "Different regionprops are equal")
+
+
 if __name__ == "__main__":
     from numpy.testing import run_module_suite
     run_module_suite()