Repository: scikit-image/scikit-image

Issue:
regionprops: consistent naming scheme
## Description
It would be nice to have a consistent naming scheme for the properties retrieved by the `measure.regionprops` function.

For instance, there are `feret_diameter_max` and `max_intensity`. It would be nice to have `intensity_max` instead. 

Other candidates are:
```
mean_intensity
min_intensity
bbox_area
convex_area
convex_image
filled_area
filled_image
intensity_image
major_axis_length
max_intensity
mean_intensity
min_intensity
minor_axis_length
weighted_centroid
weighted_local_centroid
weighted_moments
weighted_moments_central
weighted_moments_hu
weighted_moments_normalized
```

Since these changes would not be backwards compatible, they could be targeted for version 1.0.

Of course, it would also be a possibility, to rename `feret_diameter_max` to `max_feret_diameter`. However, this would prevent similar properties (i.e. all the different kinds of areas, intensities, moments and images) to be grouped together based on their name (see the discussion in https://github.com/scikit-image/scikit-image/pull/4820).



Discussion:
If this change is welcome, then I could create a PR for it.
Thanks @maxfrei750! Certainly I'd like to revisit these and I agree that for 1.0 we want to make sure we like all of the naming conventions for regionprops — one of our most important functions. We should probably wait for a bit of discussion here, from everyone and especially from @scikit-image/core, about what names they would consider changing from regionprops.
I suggest @maxfrei750 comes up with a proposal for a naming scheme, and we leave the old names in place while updating the documentation to only provide the new ones.  Then, set a TODO to update to only the new names in a couple of releases (nothing urgent).
As requested by @stefanv here is a proposal for a naming scheme:

## Naming Scheme
`{generic property}_{1st most frequent specification}_{2nd most frequent specification}_ ... _{nth most frequent specification}`

### generic property
* properties that exist multiple times 
* specifies type and shape of value that will be returned
* examples: image, intensity, moments, etc.

### 1st most frequent specification
* specification of a generic property that occurs most frequently
* example: weighted

### 2nd most frequent specification
*  specification of a generic property that occurs second-most frequently
* examples: hu, normalized

...

### Problems
1. What is the generic property? For instance `major_axis_length`: Should it be `axis_length_major` or `length_axis_major`? If we go with `axis_length_major`, should we change it to `axislength_major`, to emphasize what's the generic property, and what's the specification?
2. The frequency of specifications may change over time and would then require an API breaking change. However, IMHO, this is rather unlikely to happen anytime soon.
3. `inertia_tensor` and `inertia_tensor_eigvals` might require an exception from the naming scheme. Following the scheme, they should be named `inertia_tensor` and `eigvals_inertia_tensor`, but then they would not be grouped together in the documentation.

## Additional considerations
We could use this opportunity to rename some properties to be a little more explicit:
* `bbox` => `bounding_box`
* `eigvals` => `eigenvalues`

## Examples
```
area_bbox <= bbox_area
area_convex <= convex_area
area_filled <= filled_area

image_convex <= convex_image
image_filled <= filled_image
image_intensity <= intensity_image

intensity_max <= max_intensity
intensity_mean <= mean_intensity
intensity_min <= min_intensity

axislength_minor <= minor_axis_length
axislength_major <= major_axis_length

centroid_weighted <= weighted_centroid
centroid_weighted_local <= weighted_local_centroid

moments_weighted <= weighted_moments
moments_weighted_central <= weighted_moments_central
moments_weighted_hu<= weighted_moments_hu
moments_weighted_normalized <= weighted_moments_normalized
```
@maxfrei750 This looks great, thanks!

The one I don't find particularly intuitive is `axislength`. Perhaps we should consider returning the vectors `axis_major` and `axis_minor` of which the norm is the length currently calculated?
I also think `inertia_tensor` and `inertia_tensor_eigvals` do make sense in your new scheme.  `eigvals` is more specific than `inertia_tensor`.
@stefanv Thanks for the feedback.

> The one I don't find particularly intuitive is axislength. Perhaps we should consider returning the vectors axis_major and axis_minor of which the norm is the length currently calculated?

That would be quite powerful, because it would also allow the calculation of the tilt angle of the axis.

To not lose any functionality, we could then have:
```
axis_major
axis_minor

length_axis_major
length_axis_minor

angle_axis_major
angle_axis_minor
```

Would you advise to couple this change of functionality with the PR that implements the renaming?

> I also think inertia_tensor and inertia_tensor_eigvals do make sense in your new scheme. eigvals is more specific than inertia_tensor.

I agree that the naming matches the specificity rule. However,  `inertia_tensor_eigvals` does not satisfy the requirement that the very first part of the name "specifies type and shape of value that will be returned". Nevertheless, personally, I think that's ok.


I have the same intuition about the above: `axis` is the broadest grouping, then `major/minor`, then `length/angle`, so I'd prefer `axis_major_length`, e.g.
Agreed. I'll create a PR, when I get to it.
While working on the PR, it occurred to me that for reasons of consistency, it might be good to rename the arguments of `regionprops` and `regionprops_table`:

```
label_image => image_label
intensity_image => image_intensity
```

Maybe even 
```
extra_properties => properties_extra
```

@stefanv What do you think?

Resolving patch:
diff --git a/doc/examples/segmentation/plot_regionprops.py b/doc/examples/segmentation/plot_regionprops.py
--- a/doc/examples/segmentation/plot_regionprops.py
+++ b/doc/examples/segmentation/plot_regionprops.py
@@ -41,10 +41,10 @@
 for props in regions:
     y0, x0 = props.centroid
     orientation = props.orientation
-    x1 = x0 + math.cos(orientation) * 0.5 * props.minor_axis_length
-    y1 = y0 - math.sin(orientation) * 0.5 * props.minor_axis_length
-    x2 = x0 - math.sin(orientation) * 0.5 * props.major_axis_length
-    y2 = y0 - math.cos(orientation) * 0.5 * props.major_axis_length
+    x1 = x0 + math.cos(orientation) * 0.5 * props.axis_minor_length
+    y1 = y0 - math.sin(orientation) * 0.5 * props.axis_minor_length
+    x2 = x0 - math.sin(orientation) * 0.5 * props.axis_major_length
+    y2 = y0 - math.cos(orientation) * 0.5 * props.axis_major_length
 
     ax.plot((x0, x1), (y0, y1), '-r', linewidth=2.5)
     ax.plot((x0, x2), (y0, y2), '-r', linewidth=2.5)
@@ -67,8 +67,8 @@
 
 props = regionprops_table(label_img, properties=('centroid',
                                                  'orientation',
-                                                 'major_axis_length',
-                                                 'minor_axis_length'))
+                                                 'axis_major_length',
+                                                 'axis_minor_length'))
 
 #####################################################################
 # We now display a table of these selected properties (one region per row),
@@ -100,7 +100,7 @@
 fig.update_traces(hoverinfo='skip') # hover is only for label info
 
 props = measure.regionprops(labels, img)
-properties = ['area', 'eccentricity', 'perimeter', 'mean_intensity']
+properties = ['area', 'eccentricity', 'perimeter', 'intensity_mean']
 
 # For each label, add a filled scatter trace for its contour,
 # and display the properties of the label in the hover of this trace.
diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py
--- a/skimage/feature/peak.py
+++ b/skimage/feature/peak.py
@@ -374,7 +374,7 @@ def _prominent_peaks(image, min_xdistance=1, min_ydistance=1,
 
     # Sort the list of peaks by intensity, not left-right, so larger peaks
     # in Hough space cannot be arbitrarily suppressed by smaller neighbors
-    props = sorted(props, key=lambda x: x.max_intensity)[::-1]
+    props = sorted(props, key=lambda x: x.intensity_max)[::-1]
     coords = np.array([np.round(p.centroid) for p in props], dtype=int)
 
     img_peaks = []
diff --git a/skimage/measure/_regionprops.py b/skimage/measure/_regionprops.py
--- a/skimage/measure/_regionprops.py
+++ b/skimage/measure/_regionprops.py
@@ -16,36 +16,54 @@
 __all__ = ['regionprops', 'euler_number', 'perimeter', 'perimeter_crofton']
 
 
+# All values in this PROPS dict correspond to current scikit-image property
+# names. The keys in this PROPS dict correspond to older names used in prior
+# releases. For backwards compatibility, these older names will continue to
+# work, but will not be documented.
 PROPS = {
     'Area': 'area',
     'BoundingBox': 'bbox',
-    'BoundingBoxArea': 'bbox_area',
+    'BoundingBoxArea': 'area_bbox',
+    'bbox_area': 'area_bbox',
     'CentralMoments': 'moments_central',
     'Centroid': 'centroid',
-    'ConvexArea': 'convex_area',
+    'ConvexArea': 'area_convex',
+    'convex_area': 'area_convex',
     # 'ConvexHull',
-    'ConvexImage': 'convex_image',
+    'ConvexImage': 'image_convex',
+    'convex_image': 'image_convex',
     'Coordinates': 'coords',
     'Eccentricity': 'eccentricity',
-    'EquivDiameter': 'equivalent_diameter',
+    'EquivDiameter': 'equivalent_diameter_area',
+    'equivalent_diameter': 'equivalent_diameter_area',
     'EulerNumber': 'euler_number',
     'Extent': 'extent',
     # 'Extrema',
+    'FeretDiameter': 'feret_diameter_max',
     'FeretDiameterMax': 'feret_diameter_max',
-    'FilledArea': 'filled_area',
-    'FilledImage': 'filled_image',
+    'FilledArea': 'area_filled',
+    'filled_area': 'area_filled',
+    'FilledImage': 'image_filled',
+    'filled_image': 'image_filled',
     'HuMoments': 'moments_hu',
     'Image': 'image',
     'InertiaTensor': 'inertia_tensor',
     'InertiaTensorEigvals': 'inertia_tensor_eigvals',
-    'IntensityImage': 'intensity_image',
+    'IntensityImage': 'image_intensity',
+    'intensity_image': 'image_intensity',
     'Label': 'label',
-    'LocalCentroid': 'local_centroid',
-    'MajorAxisLength': 'major_axis_length',
-    'MaxIntensity': 'max_intensity',
-    'MeanIntensity': 'mean_intensity',
-    'MinIntensity': 'min_intensity',
-    'MinorAxisLength': 'minor_axis_length',
+    'LocalCentroid': 'centroid_local',
+    'local_centroid': 'centroid_local',
+    'MajorAxisLength': 'axis_major_length',
+    'major_axis_length': 'axis_major_length',
+    'MaxIntensity': 'intensity_max',
+    'max_intensity': 'intensity_max',
+    'MeanIntensity': 'intensity_mean',
+    'mean_intensity': 'intensity_mean',
+    'MinIntensity': 'intensity_min',
+    'min_intensity': 'intensity_min',
+    'MinorAxisLength': 'axis_minor_length',
+    'minor_axis_length': 'axis_minor_length',
     'Moments': 'moments',
     'NormalizedMoments': 'moments_normalized',
     'Orientation': 'orientation',
@@ -56,60 +74,66 @@
     'Slice': 'slice',
     'Solidity': 'solidity',
     # 'SubarrayIdx'
-    'WeightedCentralMoments': 'weighted_moments_central',
-    'WeightedCentroid': 'weighted_centroid',
-    'WeightedHuMoments': 'weighted_moments_hu',
-    'WeightedLocalCentroid': 'weighted_local_centroid',
-    'WeightedMoments': 'weighted_moments',
-    'WeightedNormalizedMoments': 'weighted_moments_normalized'
+    'WeightedCentralMoments': 'moments_weighted_central',
+    'weighted_moments_central': 'moments_weighted_central',
+    'WeightedCentroid': 'centroid_weighted',
+    'weighted_centroid': 'centroid_weighted',
+    'WeightedHuMoments': 'moments_weighted_hu',
+    'weighted_moments_hu': 'moments_weighted_hu',
+    'WeightedLocalCentroid': 'centroid_weighted_local',
+    'weighted_local_centroid': 'centroid_weighted_local',
+    'WeightedMoments': 'moments_weighted',
+    'weighted_moments': 'moments_weighted',
+    'WeightedNormalizedMoments': 'moments_weighted_normalized',
+    'weighted_moments_normalized': 'moments_weighted_normalized',
 }
 
 OBJECT_COLUMNS = {
-    'image', 'coords', 'convex_image', 'slice',
-    'filled_image', 'intensity_image'
+    'image', 'coords', 'image_convex', 'slice',
+    'image_filled', 'image_intensity'
 }
 
 COL_DTYPES = {
     'area': int,
+    'area_bbox': int,
+    'area_convex': int,
+    'area_filled': int,
+    'axis_major_length': float,
+    'axis_minor_length': float,
     'bbox': int,
-    'bbox_area': int,
-    'moments_central': float,
     'centroid': float,
-    'convex_area': int,
-    'convex_image': object,
+    'centroid_local': float,
+    'centroid_weighted': float,
+    'centroid_weighted_local': float,
     'coords': object,
     'eccentricity': float,
-    'equivalent_diameter': float,
+    'equivalent_diameter_area': float,
     'euler_number': int,
     'extent': float,
     'feret_diameter_max': float,
-    'filled_area': int,
-    'filled_image': object,
-    'moments_hu': float,
     'image': object,
+    'image_convex': object,
+    'image_filled': object,
+    'image_intensity': object,
     'inertia_tensor': float,
     'inertia_tensor_eigvals': float,
-    'intensity_image': object,
+    'intensity_max': float,
+    'intensity_mean': float,
+    'intensity_min': float,
     'label': int,
-    'local_centroid': float,
-    'major_axis_length': float,
-    'max_intensity': float,
-    'mean_intensity': float,
-    'min_intensity': float,
-    'minor_axis_length': float,
     'moments': float,
+    'moments_central': float,
+    'moments_hu': float,
     'moments_normalized': float,
+    'moments_weighted': float,
+    'moments_weighted_central': float,
+    'moments_weighted_hu': float,
+    'moments_weighted_normalized': float,
     'orientation': float,
     'perimeter': float,
     'perimeter_crofton': float,
     'slice': object,
     'solidity': float,
-    'weighted_moments_central': float,
-    'weighted_centroid': float,
-    'weighted_moments_hu': float,
-    'weighted_local_centroid': float,
-    'weighted_moments': float,
-    'weighted_moments_normalized': float
 }
 
 PROP_VALS = set(PROPS.values())
@@ -260,12 +284,12 @@ def __getattr__(self, attr):
                 if self._intensity_image is not None:
                     if self._multichannel:
                         multichannel_list = [func(self.image,
-                                                  self.intensity_image[..., i])
+                                                  self.image_intensity[..., i])
                                              for i in range(
-                            self.intensity_image.shape[-1])]
+                            self.image_intensity.shape[-1])]
                         return np.stack(multichannel_list, axis=-1)
                     else:
-                        return func(self.image, self.intensity_image)
+                        return func(self.image, self.image_intensity)
                 else:
                     raise AttributeError(
                         f"intensity image required to calculate {attr}"
@@ -299,7 +323,7 @@ def bbox(self):
                      [self.slice[i].stop for i in range(self._ndim)])
 
     @property
-    def bbox_area(self):
+    def area_bbox(self):
         return self.image.size
 
     @property
@@ -308,12 +332,12 @@ def centroid(self):
 
     @property
     @_cached
-    def convex_area(self):
-        return np.sum(self.convex_image)
+    def area_convex(self):
+        return np.sum(self.image_convex)
 
     @property
     @_cached
-    def convex_image(self):
+    def image_convex(self):
         from ..morphology.convex_hull import convex_hull_image
         return convex_hull_image(self.image)
 
@@ -332,7 +356,7 @@ def eccentricity(self):
         return sqrt(1 - l2 / l1)
 
     @property
-    def equivalent_diameter(self):
+    def equivalent_diameter_area(self):
         return (2 * self._ndim * self.area / PI) ** (1 / self._ndim)
 
     @property
@@ -348,7 +372,7 @@ def extent(self):
 
     @property
     def feret_diameter_max(self):
-        identity_convex_hull = np.pad(self.convex_image,
+        identity_convex_hull = np.pad(self.image_convex,
                                       2, mode='constant', constant_values=0)
         if self._ndim == 2:
             coordinates = np.vstack(find_contours(identity_convex_hull, .5,
@@ -359,12 +383,12 @@ def feret_diameter_max(self):
         return sqrt(np.max(distances))
 
     @property
-    def filled_area(self):
-        return np.sum(self.filled_image)
+    def area_filled(self):
+        return np.sum(self.image_filled)
 
     @property
     @_cached
-    def filled_image(self):
+    def image_filled(self):
         structure = np.ones((3,) * self._ndim)
         return ndi.binary_fill_holes(self.image, structure)
 
@@ -387,7 +411,7 @@ def inertia_tensor_eigvals(self):
 
     @property
     @_cached
-    def intensity_image(self):
+    def image_intensity(self):
         if self._intensity_image is None:
             raise AttributeError('No intensity image specified.')
         image = (
@@ -397,36 +421,36 @@ def intensity_image(self):
         )
         return self._intensity_image[self.slice] * image
 
-    def _intensity_image_double(self):
-        return self.intensity_image.astype(np.double)
+    def _image_intensity_double(self):
+        return self.image_intensity.astype(np.double)
 
     @property
-    def local_centroid(self):
+    def centroid_local(self):
         M = self.moments
         return tuple(M[tuple(np.eye(self._ndim, dtype=int))] /
                      M[(0,) * self._ndim])
 
     @property
-    def max_intensity(self):
-        return np.max(self.intensity_image[self.image], axis=0)\
+    def intensity_max(self):
+        return np.max(self.image_intensity[self.image], axis=0)\
                  .astype(np.double)
 
     @property
-    def mean_intensity(self):
-        return np.mean(self.intensity_image[self.image], axis=0)
+    def intensity_mean(self):
+        return np.mean(self.image_intensity[self.image], axis=0)
 
     @property
-    def min_intensity(self):
-        return np.min(self.intensity_image[self.image], axis=0)\
+    def intensity_min(self):
+        return np.min(self.image_intensity[self.image], axis=0)\
                  .astype(np.double)
 
     @property
-    def major_axis_length(self):
+    def axis_major_length(self):
         l1 = self.inertia_tensor_eigvals[0]
         return 4 * sqrt(l1)
 
     @property
-    def minor_axis_length(self):
+    def axis_minor_length(self):
         l2 = self.inertia_tensor_eigvals[-1]
         return 4 * sqrt(l2)
 
@@ -440,7 +464,7 @@ def moments(self):
     @_cached
     def moments_central(self):
         mu = _moments.moments_central(self.image.astype(np.uint8),
-                                      self.local_centroid, order=3)
+                                      self.centroid_local, order=3)
         return mu
 
     @property
@@ -477,24 +501,24 @@ def perimeter_crofton(self):
 
     @property
     def solidity(self):
-        return self.area / self.convex_area
+        return self.area / self.area_convex
 
     @property
-    def weighted_centroid(self):
-        ctr = self.weighted_local_centroid
+    def centroid_weighted(self):
+        ctr = self.centroid_weighted_local
         return tuple(idx + slc.start
                      for idx, slc in zip(ctr, self.slice))
 
     @property
-    def weighted_local_centroid(self):
-        M = self.weighted_moments
+    def centroid_weighted_local(self):
+        M = self.moments_weighted
         return (M[tuple(np.eye(self._ndim, dtype=int))] /
                 M[(0,) * self._ndim])
 
     @property
     @_cached
-    def weighted_moments(self):
-        image = self._intensity_image_double()
+    def moments_weighted(self):
+        image = self._image_intensity_double()
         if self._multichannel:
             moments = np.stack(
                     [_moments.moments(image[..., i], order=3)
@@ -507,9 +531,9 @@ def weighted_moments(self):
 
     @property
     @_cached
-    def weighted_moments_central(self):
-        ctr = self.weighted_local_centroid
-        image = self._intensity_image_double()
+    def moments_weighted_central(self):
+        ctr = self.centroid_weighted_local
+        image = self._image_intensity_double()
         if self._multichannel:
             moments_list = [
                 _moments.moments_central(
@@ -524,8 +548,8 @@ def weighted_moments_central(self):
 
     @property
     @only2d
-    def weighted_moments_hu(self):
-        nu = self.weighted_moments_normalized
+    def moments_weighted_hu(self):
+        nu = self.moments_weighted_normalized
         if self._multichannel:
             nchannels = self._intensity_image.shape[-1]
             return np.stack(
@@ -537,8 +561,8 @@ def weighted_moments_hu(self):
 
     @property
     @_cached
-    def weighted_moments_normalized(self):
-        mu = self.weighted_moments_central
+    def moments_weighted_normalized(self):
+        mu = self.moments_weighted_central
         if self._multichannel:
             nchannels = self._intensity_image.shape[-1]
             return np.stack(
@@ -548,22 +572,22 @@ def weighted_moments_normalized(self):
             )
         else:
             return _moments.moments_normalized(mu, order=3)
-        return _moments.moments_normalized(self.weighted_moments_central, 3)
+        return _moments.moments_normalized(self.moments_weighted_central, 3)
 
     def __iter__(self):
         props = PROP_VALS
 
         if self._intensity_image is None:
-            unavailable_props = ('intensity_image',
-                                 'max_intensity',
-                                 'mean_intensity',
-                                 'min_intensity',
-                                 'weighted_moments',
-                                 'weighted_moments_central',
-                                 'weighted_centroid',
-                                 'weighted_local_centroid',
-                                 'weighted_moments_hu',
-                                 'weighted_moments_normalized')
+            unavailable_props = ('image_intensity',
+                                 'intensity_max',
+                                 'intensity_mean',
+                                 'intensity_min',
+                                 'moments_weighted',
+                                 'moments_weighted_central',
+                                 'centroid_weighted',
+                                 'centroid_weighted_local',
+                                 'moments_weighted_hu',
+                                 'moments_weighted_normalized')
 
             props = props.difference(unavailable_props)
 
@@ -837,7 +861,7 @@ def regionprops_table(label_image, intensity_image=None,
     >>>
     >>> image = data.coins()
     >>> label_image = measure.label(image > 110, connectivity=image.ndim)
-    >>> props = measure.regionprops_table(label_image, intensity_image=image,
+    >>> props = measure.regionprops_table(label_image, image_intensity=image,
     ...                                   properties=('label',),
     ...                                   extra_properties=(quartiles,))
     >>> import pandas as pd # doctest: +SKIP
@@ -939,19 +963,35 @@ def regionprops(label_image, intensity_image=None, cache=True,
 
     **area** : int
         Number of pixels of the region.
+    **area_bbox** : int
+        Number of pixels of bounding box.
+    **area_convex** : int
+        Number of pixels of convex hull image, which is the smallest convex
+        polygon that encloses the region.
+    **area_filled** : int
+        Number of pixels of the region will all the holes filled in. Describes
+        the area of the image_filled.
+    **axis_major_length** : float
+        The length of the major axis of the ellipse that has the same
+        normalized second central moments as the region.
+    **axis_minor_length** : float
+        The length of the minor axis of the ellipse that has the same
+        normalized second central moments as the region.
     **bbox** : tuple
         Bounding box ``(min_row, min_col, max_row, max_col)``.
         Pixels belonging to the bounding box are in the half-open interval
         ``[min_row; max_row)`` and ``[min_col; max_col)``.
-    **bbox_area** : int
-        Number of pixels of bounding box.
     **centroid** : array
         Centroid coordinate tuple ``(row, col)``.
-    **convex_area** : int
-        Number of pixels of convex hull image, which is the smallest convex
-        polygon that encloses the region.
-    **convex_image** : (H, J) ndarray
-        Binary convex hull image which has the same size as bounding box.
+    **centroid_local** : array
+        Centroid coordinate tuple ``(row, col)``, relative to region bounding
+        box.
+    **centroid_weighted** : array
+        Centroid coordinate tuple ``(row, col)`` weighted with intensity
+        image.
+    **centroid_weighted_local** : array
+        Centroid coordinate tuple ``(row, col)``, relative to region bounding
+        box, weighted with intensity image.
     **coords** : (N, 2) ndarray
         Coordinate list ``(row, col)`` of the region.
     **eccentricity** : float
@@ -960,7 +1000,7 @@ def regionprops(label_image, intensity_image=None, cache=True,
         (distance between focal points) over the major axis length.
         The value is in the interval [0, 1).
         When it is 0, the ellipse becomes a circle.
-    **equivalent_diameter** : float
+    **equivalent_diameter_area** : float
         The diameter of a circle with the same area as the region.
     **euler_number** : int
         Euler characteristic of the set of non-zero pixels.
@@ -974,37 +1014,27 @@ def regionprops(label_image, intensity_image=None, cache=True,
         Maximum Feret's diameter computed as the longest distance between
         points around a region's convex hull contour as determined by
         ``find_contours``. [5]_
-    **filled_area** : int
-        Number of pixels of the region will all the holes filled in. Describes
-        the area of the filled_image.
-    **filled_image** : (H, J) ndarray
-        Binary region image with filled holes which has the same size as
-        bounding box.
     **image** : (H, J) ndarray
         Sliced binary region image which has the same size as bounding box.
+    **image_convex** : (H, J) ndarray
+        Binary convex hull image which has the same size as bounding box.
+    **image_filled** : (H, J) ndarray
+        Binary region image with filled holes which has the same size as
+        bounding box.
+    **image_intensity** : ndarray
+        Image inside region bounding box.
     **inertia_tensor** : ndarray
         Inertia tensor of the region for the rotation around its mass.
     **inertia_tensor_eigvals** : tuple
         The eigenvalues of the inertia tensor in decreasing order.
-    **intensity_image** : ndarray
-        Image inside region bounding box.
-    **label** : int
-        The label in the labeled input image.
-    **local_centroid** : array
-        Centroid coordinate tuple ``(row, col)``, relative to region bounding
-        box.
-    **major_axis_length** : float
-        The length of the major axis of the ellipse that has the same
-        normalized second central moments as the region.
-    **max_intensity** : float
+    **intensity_max** : float
         Value with the greatest intensity in the region.
-    **mean_intensity** : float
+    **intensity_mean** : float
         Value with the mean intensity in the region.
-    **min_intensity** : float
+    **intensity_min** : float
         Value with the least intensity in the region.
-    **minor_axis_length** : float
-        The length of the minor axis of the ellipse that has the same
-        normalized second central moments as the region.
+    **label** : int
+        The label in the labeled input image.
     **moments** : (3, 3) ndarray
         Spatial moments up to 3rd order::
 
@@ -1026,33 +1056,13 @@ def regionprops(label_image, intensity_image=None, cache=True,
             nu_ij = mu_ij / m_00^[(i+j)/2 + 1]
 
         where `m_00` is the zeroth spatial moment.
-    **orientation** : float
-        Angle between the 0th axis (rows) and the major
-        axis of the ellipse that has the same second moments as the region,
-        ranging from `-pi/2` to `pi/2` counter-clockwise.
-    **perimeter** : float
-        Perimeter of object which approximates the contour as a line
-        through the centers of border pixels using a 4-connectivity.
-    **perimeter_crofton** : float
-        Perimeter of object approximated by the Crofton formula in 4
-        directions.
-    **slice** : tuple of slices
-        A slice to extract the object from the source image.
-    **solidity** : float
-        Ratio of pixels in the region to pixels of the convex hull image.
-    **weighted_centroid** : array
-        Centroid coordinate tuple ``(row, col)`` weighted with intensity
-        image.
-    **weighted_local_centroid** : array
-        Centroid coordinate tuple ``(row, col)``, relative to region bounding
-        box, weighted with intensity image.
-    **weighted_moments** : (3, 3) ndarray
+    **moments_weighted** : (3, 3) ndarray
         Spatial moments of intensity image up to 3rd order::
 
             wm_ij = sum{ array(row, col) * row^i * col^j }
 
         where the sum is over the `row`, `col` coordinates of the region.
-    **weighted_moments_central** : (3, 3) ndarray
+    **moments_weighted_central** : (3, 3) ndarray
         Central moments (translation invariant) of intensity image up to
         3rd order::
 
@@ -1061,16 +1071,30 @@ def regionprops(label_image, intensity_image=None, cache=True,
         where the sum is over the `row`, `col` coordinates of the region,
         and `row_c` and `col_c` are the coordinates of the region's weighted
         centroid.
-    **weighted_moments_hu** : tuple
+    **moments_weighted_hu** : tuple
         Hu moments (translation, scale and rotation invariant) of intensity
         image.
-    **weighted_moments_normalized** : (3, 3) ndarray
+    **moments_weighted_normalized** : (3, 3) ndarray
         Normalized moments (translation and scale invariant) of intensity
         image up to 3rd order::
 
             wnu_ij = wmu_ij / wm_00^[(i+j)/2 + 1]
 
         where ``wm_00`` is the zeroth spatial moment (intensity-weighted area).
+    **orientation** : float
+        Angle between the 0th axis (rows) and the major
+        axis of the ellipse that has the same second moments as the region,
+        ranging from `-pi/2` to `pi/2` counter-clockwise.
+    **perimeter** : float
+        Perimeter of object which approximates the contour as a line
+        through the centers of border pixels using a 4-connectivity.
+    **perimeter_crofton** : float
+        Perimeter of object approximated by the Crofton formula in 4
+        directions.
+    **slice** : tuple of slices
+        A slice to extract the object from the source image.
+    **solidity** : float
+        Ratio of pixels in the region to pixels of the convex hull image.
 
     Each region also supports iteration, so that you can do::
 


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
@@ -1,19 +1,18 @@
 import math
 
 import numpy as np
-from numpy import array
+import pytest
+from numpy.testing import (assert_almost_equal, assert_array_equal,
+                           assert_array_almost_equal, assert_equal)
 
 from skimage import data
-from skimage.segmentation import slic
 from skimage._shared._warnings import expected_warnings
 from skimage.measure._regionprops import (regionprops, PROPS, perimeter,
                                           perimeter_crofton, euler_number,
                                           _parse_docs, _props_to_dict,
                                           regionprops_table, OBJECT_COLUMNS,
                                           COL_DTYPES)
-from skimage._shared import testing
-from skimage._shared.testing import (assert_array_equal, assert_almost_equal,
-                                     assert_array_almost_equal, assert_equal)
+from skimage.segmentation import slic
 
 
 SAMPLE = np.array(
@@ -63,11 +62,11 @@ def test_all_props_3d():
 def test_dtype():
     regionprops(np.zeros((10, 10), dtype=int))
     regionprops(np.zeros((10, 10), dtype=np.uint))
-    with testing.raises(TypeError):
+    with pytest.raises(TypeError):
         regionprops(np.zeros((10, 10), dtype=float))
-    with testing.raises(TypeError):
+    with pytest.raises(TypeError):
         regionprops(np.zeros((10, 10), dtype=np.double))
-    with testing.raises(TypeError):
+    with pytest.raises(TypeError):
         regionprops(np.zeros((10, 10), dtype=bool))
 
 
@@ -77,7 +76,7 @@ def test_ndim():
     regionprops(np.zeros((10, 10, 10), dtype=int))
     regionprops(np.zeros((1, 1), dtype=int))
     regionprops(np.zeros((1, 1, 1), dtype=int))
-    with testing.raises(TypeError):
+    with pytest.raises(TypeError):
         regionprops(np.zeros((10, 10, 10, 2), dtype=int))
 
 
@@ -121,9 +120,9 @@ def test_bbox():
     assert_array_almost_equal(bbox, (1, 1, 1, 4, 3, 3))
 
 
-def test_bbox_area():
+def test_area_bbox():
     padded = np.pad(SAMPLE, 5, mode='constant')
-    bbox_area = regionprops(padded)[0].bbox_area
+    bbox_area = regionprops(padded)[0].area_bbox
     assert_array_almost_equal(bbox_area, SAMPLE.size)
 
 
@@ -152,13 +151,13 @@ def test_centroid_3d():
     assert_array_almost_equal(centroid, (1.66666667, 1.55555556, 1.55555556))
 
 
-def test_convex_area():
-    area = regionprops(SAMPLE)[0].convex_area
+def test_area_convex():
+    area = regionprops(SAMPLE)[0].area_convex
     assert area == 125
 
 
-def test_convex_image():
-    img = regionprops(SAMPLE)[0].convex_image
+def test_image_convex():
+    img = regionprops(SAMPLE)[0].image_convex
     ref = np.array(
         [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
@@ -206,8 +205,8 @@ def test_eccentricity():
     assert_almost_equal(eps, 0)
 
 
-def test_equiv_diameter():
-    diameter = regionprops(SAMPLE)[0].equivalent_diameter
+def test_equivalent_diameter_area():
+    diameter = regionprops(SAMPLE)[0].equivalent_diameter_area
     # determined with MATLAB
     assert_almost_equal(diameter, 9.57461472963)
 
@@ -280,48 +279,48 @@ def test_label():
     assert_array_equal(label, 1)
 
 
-def test_filled_area():
-    area = regionprops(SAMPLE)[0].filled_area
+def test_area_filled():
+    area = regionprops(SAMPLE)[0].area_filled
     assert area == np.sum(SAMPLE)
 
     SAMPLE_mod = SAMPLE.copy()
     SAMPLE_mod[7, -3] = 0
-    area = regionprops(SAMPLE_mod)[0].filled_area
+    area = regionprops(SAMPLE_mod)[0].area_filled
     assert area == np.sum(SAMPLE)
 
 
-def test_filled_image():
-    img = regionprops(SAMPLE)[0].filled_image
+def test_image_filled():
+    img = regionprops(SAMPLE)[0].image_filled
     assert_array_equal(img, SAMPLE)
 
 
-def test_major_axis_length():
-    length = regionprops(SAMPLE)[0].major_axis_length
+def test_axis_major_length():
+    length = regionprops(SAMPLE)[0].axis_major_length
     # MATLAB has different interpretation of ellipse than found in literature,
     # here implemented as found in literature
     assert_almost_equal(length, 16.7924234999)
 
 
-def test_max_intensity():
+def test_intensity_max():
     intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                            )[0].max_intensity
+                            )[0].intensity_max
     assert_almost_equal(intensity, 2)
 
 
-def test_mean_intensity():
+def test_intensity_mean():
     intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                            )[0].mean_intensity
+                            )[0].intensity_mean
     assert_almost_equal(intensity, 1.02777777777777)
 
 
-def test_min_intensity():
+def test_intensity_min():
     intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                            )[0].min_intensity
+                            )[0].intensity_min
     assert_almost_equal(intensity, 1)
 
 
-def test_minor_axis_length():
-    length = regionprops(SAMPLE)[0].minor_axis_length
+def test_axis_minor_length():
+    length = regionprops(SAMPLE)[0].axis_minor_length
     # MATLAB has different interpretation of ellipse than found in literature,
     # here implemented as found in literature
     assert_almost_equal(length, 9.739302807263)
@@ -391,9 +390,9 @@ def test_solidity():
     assert_almost_equal(solidity, 0.576)
 
 
-def test_weighted_moments_central():
+def test_moments_weighted_central():
     wmu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                      )[0].weighted_moments_central
+                      )[0].moments_weighted_central
     ref = np.array(
         [[7.4000000000e+01, 3.7303493627e-14, 1.2602837838e+03,
           -7.6561796932e+02],
@@ -408,15 +407,15 @@ def test_weighted_moments_central():
     assert_array_almost_equal(wmu, ref)
 
 
-def test_weighted_centroid():
+def test_centroid_weighted():
     centroid = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                           )[0].weighted_centroid
+                           )[0].centroid_weighted
     assert_array_almost_equal(centroid, (5.540540540540, 9.445945945945))
 
 
-def test_weighted_moments_hu():
+def test_moments_weighted_hu():
     whu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                      )[0].weighted_moments_hu
+                      )[0].moments_weighted_hu
     ref = np.array([
         3.1750587329e-01,
         2.1417517159e-02,
@@ -429,9 +428,9 @@ def test_weighted_moments_hu():
     assert_array_almost_equal(whu, ref)
 
 
-def test_weighted_moments():
+def test_moments_weighted():
     wm = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                     )[0].weighted_moments
+                     )[0].moments_weighted
     ref = np.array(
         [[7.4000000e+01, 6.9900000e+02, 7.8630000e+03, 9.7317000e+04],
          [4.1000000e+02, 3.7850000e+03, 4.4063000e+04, 5.7256700e+05],
@@ -441,9 +440,9 @@ def test_weighted_moments():
     assert_array_almost_equal(wm, ref)
 
 
-def test_weighted_moments_normalized():
+def test_moments_weighted_normalized():
     wnu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
-                      )[0].weighted_moments_normalized
+                      )[0].moments_weighted_normalized
     ref = np.array(
         [[np.nan,        np.nan, 0.2301467830, -0.0162529732],
          [np.nan, -0.0160405109, 0.0457932622, -0.0104598869],
@@ -471,15 +470,15 @@ def test_invalid():
     ps = regionprops(SAMPLE)
 
     def get_intensity_image():
-        ps[0].intensity_image
+        ps[0].image_intensity
 
-    with testing.raises(AttributeError):
+    with pytest.raises(AttributeError):
         get_intensity_image()
 
 
 def test_invalid_size():
     wrong_intensity_sample = np.array([[1], [1]])
-    with testing.raises(ValueError):
+    with pytest.raises(ValueError):
         regionprops(SAMPLE, wrong_intensity_sample)
 
 
@@ -512,16 +511,16 @@ def test_iterate_all_props():
 def test_cache():
     SAMPLE_mod = SAMPLE.copy()
     region = regionprops(SAMPLE_mod)[0]
-    f0 = region.filled_image
+    f0 = region.image_filled
     region._label_image[:10] = 1
-    f1 = region.filled_image
+    f1 = region.image_filled
 
     # Changed underlying image, but cache keeps result the same
     assert_array_equal(f0, f1)
 
     # Now invalidate cache
     region._cache_active = False
-    f1 = region.filled_image
+    f1 = region.image_filled
 
     assert np.any(f0 != f1)
 
@@ -541,7 +540,7 @@ def foo():
     nr_props = len(props)
     if has_docstrings:
         assert_equal(nr_docs_parsed, nr_props)
-        ds = docs['weighted_moments_normalized']
+        ds = docs['moments_weighted_normalized']
         assert 'iteration' not in ds
         assert len(ds.split('\n')) > 3
     else:
@@ -551,29 +550,29 @@ def foo():
 def test_props_to_dict():
     regions = regionprops(SAMPLE)
     out = _props_to_dict(regions)
-    assert out == {'label': array([1]),
-                   'bbox-0': array([0]), 'bbox-1': array([0]),
-                   'bbox-2': array([10]), 'bbox-3': array([18])}
+    assert out == {'label': np.array([1]),
+                   'bbox-0': np.array([0]), 'bbox-1': np.array([0]),
+                   'bbox-2': np.array([10]), 'bbox-3': np.array([18])}
 
     regions = regionprops(SAMPLE)
     out = _props_to_dict(regions, properties=('label', 'area', 'bbox'),
                          separator='+')
-    assert out == {'label': array([1]), 'area': array([72]),
-                   'bbox+0': array([0]), 'bbox+1': array([0]),
-                   'bbox+2': array([10]), 'bbox+3': array([18])}
+    assert out == {'label': np.array([1]), 'area': np.array([72]),
+                   'bbox+0': np.array([0]), 'bbox+1': np.array([0]),
+                   'bbox+2': np.array([10]), 'bbox+3': np.array([18])}
 
 
 def test_regionprops_table():
     out = regionprops_table(SAMPLE)
-    assert out == {'label': array([1]),
-                   'bbox-0': array([0]), 'bbox-1': array([0]),
-                   'bbox-2': array([10]), 'bbox-3': array([18])}
+    assert out == {'label': np.array([1]),
+                   'bbox-0': np.array([0]), 'bbox-1': np.array([0]),
+                   'bbox-2': np.array([10]), 'bbox-3': np.array([18])}
 
     out = regionprops_table(SAMPLE, properties=('label', 'area', 'bbox'),
                             separator='+')
-    assert out == {'label': array([1]), 'area': array([72]),
-                   'bbox+0': array([0]), 'bbox+1': array([0]),
-                   'bbox+2': array([10]), 'bbox+3': array([18])}
+    assert out == {'label': np.array([1]), 'area': np.array([72]),
+                   'bbox+0': np.array([0]), 'bbox+1': np.array([0]),
+                   'bbox+2': np.array([10]), 'bbox+3': np.array([18])}
 
 
 def test_regionprops_table_equal_to_original():
@@ -647,9 +646,9 @@ def test_column_dtypes_correct():
 
 def test_deprecated_coords_argument():
     with expected_warnings(['coordinates keyword argument']):
-        region = regionprops(SAMPLE, coordinates='rc')
-    with testing.raises(ValueError):
-        region = regionprops(SAMPLE, coordinates='xy')
+        regionprops(SAMPLE, coordinates='rc')
+    with pytest.raises(ValueError):
+        regionprops(SAMPLE, coordinates='xy')
 
 
 def pixelcount(regionmask):
@@ -657,11 +656,11 @@ def pixelcount(regionmask):
     return np.sum(regionmask)
 
 
-def median_intensity(regionmask, intensity_image):
-    return np.median(intensity_image[regionmask])
+def intensity_median(regionmask, image_intensity):
+    return np.median(image_intensity[regionmask])
 
 
-def too_many_args(regionmask, intensity_image, superfluous):
+def too_many_args(regionmask, image_intensity, superfluous):
     return 1
 
 
@@ -676,22 +675,22 @@ def test_extra_properties():
 
 def test_extra_properties_intensity():
     region = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE,
-                         extra_properties=(median_intensity,)
+                         extra_properties=(intensity_median,)
                          )[0]
-    assert region.median_intensity == np.median(INTENSITY_SAMPLE[SAMPLE == 1])
+    assert region.intensity_median == np.median(INTENSITY_SAMPLE[SAMPLE == 1])
 
 
 def test_extra_properties_no_intensity_provided():
-    with testing.raises(AttributeError):
-        region = regionprops(SAMPLE, extra_properties=(median_intensity,))[0]
-        _ = region.median_intensity
+    with pytest.raises(AttributeError):
+        region = regionprops(SAMPLE, extra_properties=(intensity_median,))[0]
+        _ = region.intensity_median
 
 
 def test_extra_properties_nr_args():
-    with testing.raises(AttributeError):
+    with pytest.raises(AttributeError):
         region = regionprops(SAMPLE, extra_properties=(too_few_args,))[0]
         _ = region.too_few_args
-    with testing.raises(AttributeError):
+    with pytest.raises(AttributeError):
         region = regionprops(SAMPLE, extra_properties=(too_many_args,))[0]
         _ = region.too_many_args
 
@@ -699,9 +698,9 @@ def test_extra_properties_nr_args():
 def test_extra_properties_mixed():
     # mixed properties, with and without intensity
     region = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE,
-                         extra_properties=(median_intensity, pixelcount)
+                         extra_properties=(intensity_median, pixelcount)
                          )[0]
-    assert region.median_intensity == np.median(INTENSITY_SAMPLE[SAMPLE == 1])
+    assert region.intensity_median == np.median(INTENSITY_SAMPLE[SAMPLE == 1])
     assert region.pixelcount == np.sum(SAMPLE == 1)
 
 
@@ -709,10 +708,10 @@ def test_extra_properties_table():
     out = regionprops_table(SAMPLE_MULTIPLE,
                             intensity_image=INTENSITY_SAMPLE_MULTIPLE,
                             properties=('label',),
-                            extra_properties=(median_intensity, pixelcount)
+                            extra_properties=(intensity_median, pixelcount)
                             )
-    assert_array_almost_equal(out['median_intensity'], array([2., 4.]))
-    assert_array_equal(out['pixelcount'], array([10, 2]))
+    assert_array_almost_equal(out['intensity_median'], np.array([2., 4.]))
+    assert_array_equal(out['pixelcount'], np.array([10, 2]))
 
 
 def test_multichannel():
@@ -724,14 +723,14 @@ def test_multichannel():
     segment_idx = np.max(labels) // 2
     region = regionprops(labels,
                          astro_green,
-                         extra_properties=[median_intensity]
+                         extra_properties=[intensity_median]
                          )[segment_idx]
     region_multi = regionprops(labels,
                                astro,
-                               extra_properties=[median_intensity]
+                               extra_properties=[intensity_median]
                                )[segment_idx]
 
-    for prop in list(PROPS.keys()) + ["median_intensity"]:
+    for prop in list(PROPS.keys()) + ["intensity_median"]:
         p = region[prop]
         p_multi = region_multi[prop]
         if np.shape(p) == np.shape(p_multi):