Repository: scikit-image/scikit-image

Issue:
ORB.detect_and_extract fails if image too small for full pyramid
### Description:

When the supplied image is so small that `pyramid_gaussian` returns an image where shape contains 1s, `ORB.detect_and_extract` fails with: "ValueError: The parameter `image` must be a 2-dimensional array"

The first octaves in `ORB.detect_and_extract` go fine, but at `octave=5`, the error occurs.

I think this error should be avoided by checking the shape of `octave_image` and breaking the loop early if `octave_image` is too small. This behavior would be the same as if no keypoints could be detected in the current octave.

The same would apply to `ORB.detect` (but not `ORB.extract`).

### Way to reproduce:

```Python
import numpy as np
from skimage.feature import ORB

img = np.random.rand(32,32)

orb = ORB(downscale=2)

orb.detect_and_extract(img)
```


### Traceback or output:

```Shell
Traceback (most recent call last):
  File "/tmp/ipykernel_855686/2532002236.py", line 10, in <module>
    orb.detect_and_extract(img)
  File ".../venv/lib/python3.10/site-packages/skimage/feature/orb.py", line 304, in detect_and_extract
    keypoints, orientations, responses = self._detect_octave(
  File ".../venv/lib/python3.10/site-packages/skimage/feature/orb.py", line 141, in _detect_octave
    fast_response = corner_fast(octave_image, self.fast_n,
  File ".../venv/lib/python3.10/site-packages/skimage/feature/corner.py", line 836, in corner_fast
    image = _prepare_grayscale_input_2D(image)
  File ".../venv/lib/python3.10/site-packages/skimage/feature/util.py", line 143, in _prepare_grayscale_input_2D
    check_nD(image, 2)
  File ".../venv/lib/python3.10/site-packages/skimage/_shared/utils.py", line 655, in check_nD
    raise ValueError(
ValueError: The parameter `image` must be a 2-dimensional array
```


### Version information:

```Shell
3.10.6 | packaged by conda-forge | (main, Aug 22 2022, 20:36:39) [GCC 10.4.0]
Linux-5.4.0-126-generic-x86_64-with-glibc2.31
scikit-image version: 0.19.3
numpy version: 1.23.3
```



Discussion:
Thanks for reporting @moi90! Are you interested in working on a fix?

Resolving patch:
diff --git a/skimage/feature/orb.py b/skimage/feature/orb.py
--- a/skimage/feature/orb.py
+++ b/skimage/feature/orb.py
@@ -182,6 +182,10 @@ def detect(self, image):
 
             octave_image = np.ascontiguousarray(pyramid[octave])
 
+            if np.squeeze(octave_image).ndim < 2:
+                # No further keypoints can be detected if the image is not really 2d
+                break
+
             keypoints, orientations, responses = self._detect_octave(
                 octave_image)
 
@@ -301,6 +305,10 @@ def detect_and_extract(self, image):
 
             octave_image = np.ascontiguousarray(pyramid[octave])
 
+            if np.squeeze(octave_image).ndim < 2:
+                # No further keypoints can be detected if the image is not really 2d
+                break
+
             keypoints, orientations, responses = self._detect_octave(
                 octave_image)
 


Test patch:
diff --git a/skimage/feature/tests/test_orb.py b/skimage/feature/tests/test_orb.py
--- a/skimage/feature/tests/test_orb.py
+++ b/skimage/feature/tests/test_orb.py
@@ -139,3 +139,9 @@ def test_no_descriptors_extracted_orb():
     detector_extractor = ORB()
     with pytest.raises(RuntimeError):
         detector_extractor.detect_and_extract(img)
+
+def test_img_too_small_orb():
+    img = data.brick()[:64,:64]
+    detector_extractor = ORB(downscale=2, n_scales=8)
+    detector_extractor.detect(img)
+    detector_extractor.detect_and_extract(img)