Repository: astropy/photutils

Issue:
IRAFStarFinder Error: self.cutout_data_nosub is size (0,)
Hello! I am recieving an unexpected error with IRAFStarFinder. When attempting to select stars and mask them in 5x5 pixel cutouts, there will be one or more instances of the self.cutout_data_nosub being dimension (0,), thus producing a broadcasting error. 

I am unsure as to why this issue is popping up. I am using the cluster fitting code from Brown & Gnedin 2021 (https://academic.oup.com/mnras/article/508/4/5935/6384836), leading me to believe that something in the methods source code has changed since the publication was released, or there is something wrong with my environment. Any guidance would be highly appreciated! 

The code I am using is 



```
def find_stars(data_snapshot, uncertainty_snapshot):

    if data_snapshot.size == 0 or error_snapshot.size == 0:
        raise ValueError("Data snapshot or error snapshot is empty.")
    threshold = 5 * np.min(uncertainty_snapshot)
    if abs(threshold) == np.inf or threshold == 0 or threshold == np.nan:
        raise ValueError('Bad threshold value.')
    if abs(np.min(data_snapshot)) == np.inf or np.min(data_snapshot) == 0 or np.min(data_snapshot) == np.nan:
        raise ValueError('Bad data min value.')
    star_finder = photutils.detection.IRAFStarFinder(
        threshold=threshold + np.min(data_snapshot),
        fwhm=2.0,  # slightly larger than real PSF, to get extended sources
        exclude_border=True,
        sharplo=0.8,
        sharphi=5,
        roundlo=0.0,
        roundhi=0.5,
        minsep_fwhm=1.0,
    )
    peaks_table = star_finder.find_stars(data_snapshot)

    # this will be None if nothing was found
    names = [
        "x",
        "y",
        "mask_radius",
        "near_cluster",
        "is_cluster",
    ]
    if peaks_table is None:
        return table.Table([[]] * len(names), names=names)

    # throw away some stars
    to_remove = []
    for idx in range(len(peaks_table)):
        row = peaks_table[idx]
        if (
            # throw away things with large FWHM - are probably clusters!
            row["fwhm"] * star_radius_fwhm_multiplier > cluster_mask_radius
            or row["peak"] < row["sky"]
            # peak is sky-subtracted. This ^ removes ones that aren't very far above
            # a high sky background. This cut stops substructure in clusters from
            # being masked.
            or row["peak"] < threshold
        ):
            to_remove.append(idx)
    peaks_table.remove_rows(to_remove)

    xs = peaks_table["xcentroid"].data
    ys = peaks_table["ycentroid"].data
    mask_radius = peaks_table["fwhm"].data * star_radius_fwhm_multiplier
    blank = np.ones(xs.shape) * -1

    return table.Table([xs, ys, mask_radius, blank, blank], names=names)
```
and the error:

```
ValueError                                Traceback (most recent call last)
Cell In[14], [line 203](vscode-notebook-cell:?execution_count=14&line=203)
    [201](vscode-notebook-cell:?execution_count=14&line=201) error_snapshot = sigma_data[y_min:y_max, x_min:x_max].copy()
    [202](vscode-notebook-cell:?execution_count=14&line=202) # find the stars
--> [203](vscode-notebook-cell:?execution_count=14&line=203) these_stars = find_stars(data_snapshot, error_snapshot)
    [204](vscode-notebook-cell:?execution_count=14&line=204) # change the x and y coordinates from the snapshot coords to image coords
    [205](vscode-notebook-cell:?execution_count=14&line=205) these_stars["x"] += x_min

Cell In[14], [line 66](vscode-notebook-cell:?execution_count=14&line=66)
     [55](vscode-notebook-cell:?execution_count=14&line=55)     raise ValueError('Bad data min value.')
     [56](vscode-notebook-cell:?execution_count=14&line=56) star_finder = photutils.detection.IRAFStarFinder(
     [57](vscode-notebook-cell:?execution_count=14&line=57)     threshold=threshold + np.min(data_snapshot),
     [58](vscode-notebook-cell:?execution_count=14&line=58)     fwhm=2.0,  # slightly larger than real PSF, to get extended sources
   (...)
     [64](vscode-notebook-cell:?execution_count=14&line=64)     minsep_fwhm=1.0,
     [65](vscode-notebook-cell:?execution_count=14&line=65) )
---> [66](vscode-notebook-cell:?execution_count=14&line=66) peaks_table = star_finder.find_stars(data_snapshot)
     [68](vscode-notebook-cell:?execution_count=14&line=68) # this will be None if nothing was found
     [69](vscode-notebook-cell:?execution_count=14&line=69) names = [
     [70](vscode-notebook-cell:?execution_count=14&line=70)     "x",
     [71](vscode-notebook-cell:?execution_count=14&line=71)     "y",
   (...)
     [74](vscode-notebook-cell:?execution_count=14&line=74)     "is_cluster",
     [75](vscode-notebook-cell:?execution_count=14&line=75) ]

File /opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:275, in IRAFStarFinder.find_stars(self, data, mask)
    [272](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:272)     return None
    [274](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:274) # apply all selection filters
--> [275](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:275) cat = cat.apply_all_filters()
    [276](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:276) if cat is None:
    [277](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:277)     return None

File /opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:645, in _IRAFStarFinderCatalog.apply_all_filters(self)
    [640](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:640) def apply_all_filters(self):
    [641](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:641)     """
    [642](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:642)     Apply all filters, select the brightest, and reset the source
    [643](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:643)     IDs.
    [644](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:644)     """
--> [645](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:645)     cat = self.apply_filters()
    [646](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:646)     if cat is None:
    [647](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:647)         return None

File /opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:603, in _IRAFStarFinderCatalog.apply_filters(self)
    [600](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:600) # remove all non-finite values - consider these non-detections
    [601](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:601) attrs = ('xcentroid', 'ycentroid', 'sharpness', 'roundness', 'pa',
    [602](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:602)          'sky', 'peak', 'flux')
--> [603](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:603) mask = np.count_nonzero(self.cutout_data, axis=(1, 2)) > 1
    [604](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:604) for attr in attrs:
    [605](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:605)     mask &= np.isfinite(getattr(self, attr))

File /opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:836, in lazyproperty.__get__(self, obj, owner)
    [834](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:834)         val = obj_dict.get(self._key, _NotFound)
    [835](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:835)         if val is _NotFound:
--> [836](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:836)             val = self.fget(obj)
    [837](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:837)             obj_dict[self._key] = val
    [838](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:838) return val

File /opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:475, in _IRAFStarFinderCatalog.cutout_data(self)
    [473](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:473) @lazyproperty
    [474](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:474) def cutout_data(self):
--> [475](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:475)     data = ((self.cutout_data_nosub - self.sky[:, np.newaxis, np.newaxis])
    [476](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:476)             * self.kernel.mask)
    [477](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:477)     # IRAF starfind discards negative pixels
    [478](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:478)     data[data < 0] = 0.0

File /opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:836, in lazyproperty.__get__(self, obj, owner)
    [834](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:834)         val = obj_dict.get(self._key, _NotFound)
    [835](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:835)         if val is _NotFound:
--> [836](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:836)             val = self.fget(obj)
    [837](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:837)             obj_dict[self._key] = val
    [838](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/astropy/utils/decorators.py:838) return val

File /opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:448, in _IRAFStarFinderCatalog.sky(self)
    [445](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:445)         sky = (np.max(self.cutout_data_nosub, axis=axis)
    [446](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:446)                - np.max(self.cutout_convdata, axis=axis))
    [447](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:447)     else:
--> [448](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:448)         sky = (np.sum(self.cutout_data_nosub * skymask, axis=axis)
    [449](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:449)                / nsky)
    [450](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:450) else:
    [451](https://file+.vscode-resource.vscode-cdn.net/opt/anaconda3/lib/python3.12/site-packages/photutils/detection/irafstarfinder.py:451)     sky = np.full(len(self), fill_value=self._sky)
ValueError: operands could not be broadcast together with shapes (0,) (5,5)
```


Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 229d05c8..b7729d9e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -18,6 +18,14 @@ Bug Fixes
     using the ``BkgIDWInterpolator`` and when any mesh was excluded,
     e.g., due to an input mask. [#1940]
 
+- ``photutils.detection``
+
+  - Fixed a bug in the star finders (``DAOStarFinder``,
+    ``IRAFStarFinder``, and ``StarFinder``) when
+    ``exclude_border=True``. Also, fixed an issue with
+    ``exclude_border=True`` where if all sources were in the border
+    region then an error would be raised. [#1943]
+
 
 2.0.1 (2024-10-16)
 ------------------
diff --git a/photutils/detection/core.py b/photutils/detection/core.py
index 446d70c9..83fa5086 100644
--- a/photutils/detection/core.py
+++ b/photutils/detection/core.py
@@ -39,8 +39,9 @@ class StarFinderBase(metaclass=abc.ABCMeta):
         convolved_data : 2D array_like
             The convolved 2D array.
 
-        kernel : `_StarFinderKernel`
-            The convolution kernel.
+        kernel : `_StarFinderKernel` or 2D `~numpy.ndarray`
+            The convolution kernel. ``StarFinder`` inputs the kernel
+            as a 2D array.
 
         threshold : float
             The absolute image value above which to select sources. This
@@ -83,46 +84,29 @@ class StarFinderBase(metaclass=abc.ABCMeta):
             footprint = np.array((xx**2 + yy**2) <= min_separation**2,
                                  dtype=int)
 
-        # pad the convolved data and mask by half the kernel size (or
-        # x/y radius) to allow for detections near the edges
-        if isinstance(kernel, np.ndarray):
-            ypad = (kernel.shape[0] - 1) // 2
-            xpad = (kernel.shape[1] - 1) // 2
+        # define the border exclusion region
+        if exclude_border:
+            if isinstance(kernel, np.ndarray):
+                yborder = (kernel.shape[0] - 1) // 2
+                xborder = (kernel.shape[1] - 1) // 2
+            else:
+                yborder = kernel.yradius
+                xborder = kernel.xradius
+            border_width = max(xborder, yborder)
         else:
-            ypad = kernel.yradius
-            xpad = kernel.xradius
-
-        if not exclude_border:
-            pad = ((ypad, ypad), (xpad, xpad))
-            pad_mode = 'constant'
-            convolved_data = np.pad(convolved_data, pad, mode=pad_mode,
-                                    constant_values=0.0)
-            if mask is not None:
-                mask = np.pad(mask, pad, mode=pad_mode, constant_values=False)
+            border_width = 0
 
         # find local peaks in the convolved data
         # suppress any NoDetectionsWarning from find_peaks
         with warnings.catch_warnings():
             warnings.filterwarnings('ignore', category=NoDetectionsWarning)
             tbl = find_peaks(convolved_data, threshold, footprint=footprint,
-                             mask=mask)
+                             mask=mask, border_width=border_width)
 
         if tbl is None:
             return None
 
-        if exclude_border:
-            xmax = convolved_data.shape[1] - xpad
-            ymax = convolved_data.shape[0] - ypad
-            mask = ((tbl['x_peak'] > xpad) & (tbl['y_peak'] > ypad)
-                    & (tbl['x_peak'] < xmax) & (tbl['y_peak'] < ymax))
-            tbl = tbl[mask]
-
-        xpos, ypos = tbl['x_peak'], tbl['y_peak']
-        if not exclude_border:
-            xpos -= xpad
-            ypos -= ypad
-
-        return np.transpose((xpos, ypos))
+        return np.transpose((tbl['x_peak'], tbl['y_peak']))
 
     @abc.abstractmethod
     def find_stars(self, data, mask=None):
diff --git a/photutils/detection/irafstarfinder.py b/photutils/detection/irafstarfinder.py
index 985a1446..1be0b287 100644
--- a/photutils/detection/irafstarfinder.py
+++ b/photutils/detection/irafstarfinder.py
@@ -416,7 +416,7 @@ class _IRAFStarFinderCatalog:
         skymask = ~self.kernel.mask.astype(bool)  # 1=sky, 0=obj
         nsky = np.count_nonzero(skymask)
         axis = (1, 2)
-        if nsky == 0.0:
+        if nsky == 0.0:  # pragma: no cover
             sky = (np.max(self.cutout_data_nosub, axis=axis)
                    - np.max(self.cutout_convdata, axis=axis))
         else:
@@ -452,7 +452,7 @@ class _IRAFStarFinderCatalog:
         return data
 
     @lazyproperty
-    def cutout_convdata(self):
+    def cutout_convdata(self):  # pragma: no cover
         return self.make_cutouts(self.convolved_data)
 
     @lazyproperty
diff --git a/photutils/detection/peakfinder.py b/photutils/detection/peakfinder.py
index 2cb67b05..bca74c47 100644
--- a/photutils/detection/peakfinder.py
+++ b/photutils/detection/peakfinder.py
@@ -19,7 +19,7 @@ __all__ = ['find_peaks']
 
 
 def find_peaks(data, threshold, *, box_size=3, footprint=None, mask=None,
-               border_width=None, npeaks=np.inf, centroid_func=None,
+               border_width=0, npeaks=np.inf, centroid_func=None,
                error=None, wcs=None):
     """
     Find local peaks in an image that are above a specified threshold
@@ -74,9 +74,9 @@ def find_peaks(data, threshold, *, box_size=3, footprint=None, mask=None,
         A boolean mask with the same shape as ``data``, where a `True`
         value indicates the corresponding element of ``data`` is masked.
 
-    border_width : bool, optional
+    border_width : int, optional
         The width in pixels to exclude around the border of the
-        ``data``.
+        ``data``. Must be an non-negative integer.
 
     npeaks : int, optional
         The maximum number of peaks to return. When the number of
@@ -126,7 +126,6 @@ def find_peaks(data, threshold, *, box_size=3, footprint=None, mask=None,
     arrays, unit = process_quantities((data, threshold, error),
                                       ('data', 'threshold', 'error'))
     data, threshold, error = arrays
-
     data = np.asanyarray(data)
 
     if np.all(data == data.flat[0]):
@@ -140,6 +139,11 @@ def find_peaks(data, threshold, *, box_size=3, footprint=None, mask=None,
             raise ValueError('A threshold array must have the same shape as '
                              'the input data.')
 
+    if border_width < 0:
+        raise ValueError('border_width must be a non-negative integer.')
+    if int(border_width) != border_width:
+        raise ValueError('border_width must be an integer.')
+
     # remove NaN values to avoid runtime warnings
     nan_mask = np.isnan(data)
     if np.any(nan_mask):
@@ -161,7 +165,7 @@ def find_peaks(data, threshold, *, box_size=3, footprint=None, mask=None,
             raise ValueError('data and mask must have the same shape')
         peak_goodmask = np.logical_and(peak_goodmask, ~mask)
 
-    if border_width is not None:
+    if border_width > 0:
         for i in range(peak_goodmask.ndim):
             peak_goodmask = peak_goodmask.swapaxes(0, i)
             peak_goodmask[:border_width] = False
diff --git a/photutils/detection/starfinder.py b/photutils/detection/starfinder.py
index ba8b94f6..4fd9a7af 100644
--- a/photutils/detection/starfinder.py
+++ b/photutils/detection/starfinder.py
@@ -96,7 +96,8 @@ class StarFinder(StarFinderBase):
         kernel = self.kernel
         kernel /= np.max(kernel)  # normalize max value to 1.0
         denom = np.sum(kernel**2) - (np.sum(kernel)**2 / kernel.size)
-        kernel = (kernel - np.sum(kernel) / kernel.size) / denom
+        if denom > 0:
+            kernel = (kernel - np.sum(kernel) / kernel.size) / denom
 
         convolved_data = _filter_data(data, kernel, mode='constant',
                                       fill_value=0.0,


Test patch:
diff --git a/photutils/detection/tests/test_irafstarfinder.py b/photutils/detection/tests/test_irafstarfinder.py
index 6f28676d..351b6526 100644
--- a/photutils/detection/tests/test_irafstarfinder.py
+++ b/photutils/detection/tests/test_irafstarfinder.py
@@ -9,6 +9,7 @@ import pytest
 from numpy.testing import assert_array_equal
 
 from photutils.detection import IRAFStarFinder
+from photutils.psf import CircularGaussianPRF
 from photutils.utils.exceptions import NoDetectionsWarning
 
 
@@ -171,3 +172,30 @@ class TestIRAFStarFinder:
         assert cat.isscalar
         flux = cat.flux[0]  # evaluate the flux so it can be sliced
         assert cat[0].flux == flux
+
+    def test_all_border_sources(self):
+        model1 = CircularGaussianPRF(flux=100, x_0=1, y_0=1, fwhm=2)
+        model2 = CircularGaussianPRF(flux=100, x_0=50, y_0=50, fwhm=2)
+        model3 = CircularGaussianPRF(flux=100, x_0=30, y_0=30, fwhm=2)
+
+        threshold = 1
+        yy, xx = np.mgrid[:51, :51]
+        data = model1(xx, yy)
+
+        # test single source within the border region
+        finder = IRAFStarFinder(threshold=threshold, fwhm=2.0, roundlo=-0.1,
+                                exclude_border=True)
+        with pytest.warns(NoDetectionsWarning):
+            tbl = finder(data)
+        assert tbl is None
+
+        # test multiple sources all within the border region
+        data += model2(xx, yy)
+        with pytest.warns(NoDetectionsWarning):
+            tbl = finder(data)
+        assert tbl is None
+
+        # test multiple sources with some within the border region
+        data += model3(xx, yy)
+        tbl = finder(data)
+        assert len(tbl) == 1
diff --git a/photutils/detection/tests/test_peakfinder.py b/photutils/detection/tests/test_peakfinder.py
index 834a1afd..1a1b9d1d 100644
--- a/photutils/detection/tests/test_peakfinder.py
+++ b/photutils/detection/tests/test_peakfinder.py
@@ -91,6 +91,13 @@ class TestFindPeaks:
         tbl1 = find_peaks(data, 0.1, box_size=3, border_width=25)
         assert len(tbl1) < len(tbl0)
 
+        match = 'border_width must be a non-negative integer'
+        with pytest.raises(ValueError, match=match):
+            find_peaks(data, 0.1, box_size=3, border_width=-1)
+        match = 'border_width must be an integer'
+        with pytest.raises(ValueError, match=match):
+            find_peaks(data, 0.1, box_size=3, border_width=3.1)
+
     def test_box_size_int(self, data):
         """
         Test non-integer box_size.
diff --git a/photutils/detection/tests/test_starfinder.py b/photutils/detection/tests/test_starfinder.py
index fb4df0d4..1eea5f58 100644
--- a/photutils/detection/tests/test_starfinder.py
+++ b/photutils/detection/tests/test_starfinder.py
@@ -46,6 +46,18 @@ class TestStarFinder:
         with pytest.raises(ValueError, match=match):
             StarFinder(1, kernel, brightest=3.1)
 
+    def test_exclude_border(self, data, kernel):
+        data = np.zeros((12, 12))
+        data[0:2, 0:2] = 1
+        data[9:12, 9:12] = 1
+        kernel = np.ones((3, 3))
+
+        finder0 = StarFinder(1, kernel, exclude_border=False)
+        finder1 = StarFinder(1, kernel, exclude_border=True)
+        tbl0 = finder0(data)
+        tbl1 = finder1(data)
+        assert len(tbl0) > len(tbl1)
+
     def test_nosources(self, data, kernel):
         match = 'No sources were found'
         with pytest.warns(NoDetectionsWarning, match=match):