Skip to content

Commit

Permalink
Fix --affine CLI parameter (#244)
Browse files Browse the repository at this point in the history
* Fix --affine CLI parameter

* add tests and validations for the fix so this can no happen unintentionally again

* make all too long errors and warnings one liner. Line breaks mess up with tests and are not nice for the user

---------

Co-authored-by: Lukas Weber <[email protected]>
  • Loading branch information
mstenta and lukasalexanderweber authored Oct 14, 2024
1 parent a471af1 commit da14e49
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 17 deletions.
4 changes: 3 additions & 1 deletion stitching/cli/stitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ def create_parser():
"--affine",
action="store_true",
help="Overwrites multiple parameters to optimize the stitching for "
"scans and images captured by specialized devices.",
"scans and images captured by specialized devices. The follwing parameters "
"are set: " + str(AffineStitcher.AFFINE_DEFAULTS),
)
parser.add_argument(
"--medium_megapix",
Expand Down Expand Up @@ -315,6 +316,7 @@ def main():
affine_mode = args_dict.pop("affine")

if affine_mode:
args_dict.update(AffineStitcher.AFFINE_DEFAULTS)
stitcher = AffineStitcher(**args_dict)
else:
stitcher = Stitcher(**args_dict)
Expand Down
4 changes: 1 addition & 3 deletions stitching/cropper.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ def estimate_largest_interior_rectangle(self, mask):
contours, hierarchy = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
if not hierarchy.shape == (1, 1, 4) or not np.all(hierarchy == -1):
raise StitchingError(
"""Invalid Contour. Run with --no-crop (using the stitch interface),
crop=false (using the stitcher class) or Cropper(False)
(using the cropper class)"""
"Invalid Contour. Run with --no-crop (using the stitch interface), crop=false (using the stitcher class) or Cropper(False) (using the cropper class)" # noqa: E501
)
contour = contours[0][:, 0, :]

Expand Down
3 changes: 1 addition & 2 deletions stitching/seam_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ def blend_seam_masks(
def colored_img_generator(sizes, colors):
if len(sizes) + 1 > len(colors):
warnings.warn(
"""Without additional colors,
there will be seam masks with identical colors""",
"Without additional colors, there will be seam masks with identical colors", # noqa: E501
StitchingWarning,
)

Expand Down
12 changes: 11 additions & 1 deletion stitching/stitcher.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
from types import SimpleNamespace

from .blender import Blender
Expand All @@ -10,7 +11,7 @@
from .feature_matcher import FeatureMatcher
from .images import Images
from .seam_finder import SeamFinder
from .stitching_error import StitchingError
from .stitching_error import StitchingError, StitchingWarning
from .subsetter import Subsetter
from .timelapser import Timelapser
from .verbose import verbose_stitching
Expand Down Expand Up @@ -275,3 +276,12 @@ class AffineStitcher(Stitcher):

DEFAULT_SETTINGS = Stitcher.DEFAULT_SETTINGS.copy()
DEFAULT_SETTINGS.update(AFFINE_DEFAULTS)

def initialize_stitcher(self, **kwargs):
for key, value in kwargs.items():
if key in self.AFFINE_DEFAULTS and value != self.AFFINE_DEFAULTS[key]:
warnings.warn(
f"You are overwriting an affine default ({key}={self.AFFINE_DEFAULTS[key]}) with another value ({value}). Make sure this is intended", # noqa: E501
StitchingWarning,
)
super().initialize_stitcher(**kwargs)
12 changes: 2 additions & 10 deletions stitching/subsetter.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ def subset(self, img_names, features, matches):

if len(indices) < len(img_names):
warnings.warn(
"""Not all images are included in the final panorama.
If this is not intended, use the 'matches_graph_dot_file'
parameter to analyze your matches. You might want to
lower the 'confidence_threshold' or try another 'detector'
to include all your images.""",
"Not all images are included in the final panorama. If this is not intended, use the 'matches_graph_dot_file' parameter to analyze your matches. You might want to lower the 'confidence_threshold' or try another 'detector' to include all your images.", # noqa: E501
StitchingWarning,
)

Expand Down Expand Up @@ -66,11 +62,7 @@ def get_indices_to_keep(self, features, pairwise_matches):

if len(indices) < 2:
raise StitchingError(
"""No match exceeds the given confidence threshold.
Do your images have enough overlap and common
features? If yes, you might want to lower the
'confidence_threshold' or try another
'detector'."""
"No match exceeds the given confidence threshold. Do your images have enough overlap and common features? If yes, you might want to lower the 'confidence_threshold' or try another 'detector'." # noqa: E501
)

return indices
Expand Down
21 changes: 21 additions & 0 deletions tests/test_stitch_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,27 @@ def test_main_verbose(self):
img.shape[:2], (150, 590), atol=max_image_shape_derivation
)

def test_main_affine(self):
output = test_output("budapest_from_cli.jpg")
test_args = [
"stitch.py",
test_input("budapest?.jpg"),
"--affine",
"--detector",
"sift",
"--no-crop",
"--output",
output,
]
with patch.object(sys, "argv", test_args):
main()

img = cv.imread(output)
max_image_shape_derivation = 50
np.testing.assert_allclose(
img.shape[:2], (1155, 2310), atol=max_image_shape_derivation
)

def test_main_feature_masks(self):
output = test_output("features_with_mask_from_cli.jpg")
test_args = [
Expand Down
9 changes: 9 additions & 0 deletions tests/test_stitcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ def test_stitcher_boat_aquaduct_subset(self):
graph_content = file.read()
self.assertTrue(graph_content.startswith("graph matches_graph{"))

def test_affine_stitcher_warning(self):
with self.assertWarns(StitchingWarning) as cm:
AffineStitcher(estimator="homography")
self.assertTrue(
str(cm.warning).startswith(
"You are overwriting an affine default (estimator=affine)"
)
)

def test_affine_stitcher_budapest(self):
settings = {
"detector": "sift",
Expand Down

0 comments on commit da14e49

Please sign in to comment.