| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import numpy as np
- from ..util import img_as_float
- from .._shared.utils import (
- _supported_float_type,
- check_nD,
- )
- class FeatureDetector:
- def __init__(self):
- self.keypoints_ = np.array([])
- def detect(self, image):
- """Detect keypoints in image.
- Parameters
- ----------
- image : 2D array
- Input image.
- """
- raise NotImplementedError()
- class DescriptorExtractor:
- def __init__(self):
- self.descriptors_ = np.array([])
- def extract(self, image, keypoints):
- """Extract feature descriptors in image for given keypoints.
- Parameters
- ----------
- image : 2D array
- Input image.
- keypoints : (N, 2) array
- Keypoint locations as ``(row, col)``.
- """
- raise NotImplementedError()
- def plot_matched_features(
- image0,
- image1,
- *,
- keypoints0,
- keypoints1,
- matches,
- ax,
- keypoints_color='k',
- matches_color=None,
- only_matches=False,
- alignment='horizontal',
- ):
- """Plot matched features between two images.
- .. versionadded:: 0.23
- Parameters
- ----------
- image0 : (N, M [, 3]) array
- First image.
- image1 : (N, M [, 3]) array
- Second image.
- keypoints0 : (K1, 2) array
- First keypoint coordinates as ``(row, col)``.
- keypoints1 : (K2, 2) array
- Second keypoint coordinates as ``(row, col)``.
- matches : (Q, 2) array
- Indices of corresponding matches in first and second sets of
- descriptors, where `matches[:, 0]` (resp. `matches[:, 1]`) contains
- the indices in the first (resp. second) set of descriptors.
- ax : matplotlib.axes.Axes
- The Axes object where the images and their matched features are drawn.
- keypoints_color : matplotlib color, optional
- Color for keypoint locations.
- matches_color : matplotlib color or sequence thereof, optional
- Single color or sequence of colors for each line defined by `matches`,
- which connect keypoint matches. See [1]_ for an overview of supported
- color formats. By default, colors are picked randomly.
- only_matches : bool, optional
- Set to True to plot matches only and not the keypoint locations.
- alignment : {'horizontal', 'vertical'}, optional
- Whether to show the two images side by side (`'horizontal'`), or one above
- the other (`'vertical'`).
- References
- ----------
- .. [1] https://matplotlib.org/stable/users/explain/colors/colors.html#specifying-colors
- Notes
- -----
- To make a sequence of colors passed to `matches_color` work for any number of
- `matches`, you can wrap that sequence in :func:`itertools.cycle`.
- """
- image0 = img_as_float(image0)
- image1 = img_as_float(image1)
- new_shape0 = list(image0.shape)
- new_shape1 = list(image1.shape)
- if image0.shape[0] < image1.shape[0]:
- new_shape0[0] = image1.shape[0]
- elif image0.shape[0] > image1.shape[0]:
- new_shape1[0] = image0.shape[0]
- if image0.shape[1] < image1.shape[1]:
- new_shape0[1] = image1.shape[1]
- elif image0.shape[1] > image1.shape[1]:
- new_shape1[1] = image0.shape[1]
- if new_shape0 != image0.shape:
- new_image0 = np.zeros(new_shape0, dtype=image0.dtype)
- new_image0[: image0.shape[0], : image0.shape[1]] = image0
- image0 = new_image0
- if new_shape1 != image1.shape:
- new_image1 = np.zeros(new_shape1, dtype=image1.dtype)
- new_image1[: image1.shape[0], : image1.shape[1]] = image1
- image1 = new_image1
- offset = np.array(image0.shape)
- if alignment == 'horizontal':
- image = np.concatenate([image0, image1], axis=1)
- offset[0] = 0
- elif alignment == 'vertical':
- image = np.concatenate([image0, image1], axis=0)
- offset[1] = 0
- else:
- mesg = (
- f"`plot_matched_features` accepts either 'horizontal' or 'vertical' for "
- f"alignment, but '{alignment}' was given. See "
- f"https://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.plot_matched_features "
- f"for details."
- )
- raise ValueError(mesg)
- if not only_matches:
- ax.scatter(
- keypoints0[:, 1],
- keypoints0[:, 0],
- facecolors='none',
- edgecolors=keypoints_color,
- )
- ax.scatter(
- keypoints1[:, 1] + offset[1],
- keypoints1[:, 0] + offset[0],
- facecolors='none',
- edgecolors=keypoints_color,
- )
- ax.imshow(image, cmap='gray')
- ax.axis((0, image0.shape[1] + offset[1], image0.shape[0] + offset[0], 0))
- number_of_matches = matches.shape[0]
- from matplotlib.colors import is_color_like
- if matches_color is None:
- rng = np.random.default_rng(seed=0)
- colors = [rng.random(3) for _ in range(number_of_matches)]
- elif is_color_like(matches_color):
- colors = [matches_color for _ in range(number_of_matches)]
- elif hasattr(matches_color, "__len__") and len(matches_color) == number_of_matches:
- # No need to check each color, matplotlib does so for us
- colors = matches_color
- else:
- error_message = (
- '`matches_color` needs to be a single color '
- 'or a sequence of length equal to the number of matches.'
- )
- raise ValueError(error_message)
- for i, match in enumerate(matches):
- idx0, idx1 = match
- ax.plot(
- (keypoints0[idx0, 1], keypoints1[idx1, 1] + offset[1]),
- (keypoints0[idx0, 0], keypoints1[idx1, 0] + offset[0]),
- '-',
- color=colors[i],
- )
- def _prepare_grayscale_input_2D(image):
- image = np.squeeze(image)
- check_nD(image, 2)
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- return image.astype(float_dtype, copy=False)
- def _prepare_grayscale_input_nD(image):
- image = np.squeeze(image)
- check_nD(image, range(2, 6))
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- return image.astype(float_dtype, copy=False)
- def _mask_border_keypoints(image_shape, keypoints, distance):
- """Mask coordinates that are within certain distance from the image border.
- Parameters
- ----------
- image_shape : (2,) array_like
- Shape of the image as ``(rows, cols)``.
- keypoints : (N, 2) array
- Keypoint coordinates as ``(rows, cols)``.
- distance : int
- Image border distance.
- Returns
- -------
- mask : (N,) bool array
- Mask indicating if pixels are within the image (``True``) or in the
- border region of the image (``False``).
- """
- rows = image_shape[0]
- cols = image_shape[1]
- mask = (
- ((distance - 1) < keypoints[:, 0])
- & (keypoints[:, 0] < (rows - distance + 1))
- & ((distance - 1) < keypoints[:, 1])
- & (keypoints[:, 1] < (cols - distance + 1))
- )
- return mask
|