Skip to content

Commit 6926e24

Browse files
run pre-commit hooks against all files (#12)
1 parent 212dbfb commit 6926e24

26 files changed

Lines changed: 629 additions & 491 deletions

.pre-commit-config.yaml

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Apply to all files without commiting:
2+
# pre-commit run --all-files
3+
# Update this file:
4+
# pre-commit autoupdate
5+
16
repos:
27
- repo: https://github.com/pre-commit/pre-commit-hooks
38
rev: v4.2.0
@@ -6,15 +11,7 @@ repos:
611
- id: trailing-whitespace
712
- id: end-of-file-fixer
813
- id: mixed-line-ending
9-
- id: check-symlinks
1014
- id: check-added-large-files
11-
- id: check-json
12-
- id: check-symlinks
13-
- id: check-added-large-files
14-
- repo: https://github.com/pre-commit/mirrors-prettier
15-
rev: "v2.6.2"
16-
hooks:
17-
- id: prettier
1815
- repo: https://github.com/psf/black
1916
rev: 22.3.0
2017
hooks:
@@ -27,12 +24,13 @@ repos:
2724
rev: 3.9.2
2825
hooks:
2926
- id: flake8
30-
- repo: https://github.com/detailyang/pre-commit-shell
31-
rev: 1.0.5
32-
hooks:
33-
- id: shell-lint
34-
args: [--format=json]
27+
args:
28+
- "--max-line-length=88"
29+
- "--ignore=E203"
3530
- repo: https://github.com/timothycrosley/isort
3631
rev: 5.10.1
3732
hooks:
3833
- id: isort
34+
args:
35+
- "--profile=black"
36+
- "--filter-files"

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@ pip install stitching
2222

2323
## Usage
2424

25-
The command line interface ([cli](https://github.com/lukasalexanderweber/stitching/tree/main/stitching/cli/stitch.py)) is available after installation
25+
The command line interface
26+
([cli](https://github.com/lukasalexanderweber/stitching/tree/main/stitching/cli/stitch.py))
27+
is available after installation
2628

2729
`stitch -h` show the help
2830

2931
`stitch *` stitches all files in the actual dir
3032

31-
`stitch img_dir/IMG*.jpg` stitches all files in the img_dir directory starting with "IMG" and ending with ".jpg"
33+
`stitch img_dir/IMG*.jpg` stitches all files in the img_dir directory
34+
starting with "IMG" and ending with ".jpg"
3235

33-
`stitch img1.jpg img2.jpg img3.jpg`
36+
`stitch img1.jpg img2.jpg img3.jpg`
3437
stitches the 3 explicit files of the current dir
3538

3639
Or use the Stitcher class in your script

stitching/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .stitcher import Stitcher
1+
from .stitcher import Stitcher # noqa: F401
22

33
__version__ = "0.2.0"

stitching/blender.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,37 @@
33

44

55
class Blender:
6-
"""https://docs.opencv.org/4.x/d6/d4a/classcv_1_1detail_1_1Blender.html""" # noqa
7-
8-
BLENDER_CHOICES = ('multiband', 'feather', 'no',)
9-
DEFAULT_BLENDER = 'multiband'
6+
"""https://docs.opencv.org/4.x/d6/d4a/classcv_1_1detail_1_1Blender.html"""
7+
8+
BLENDER_CHOICES = (
9+
"multiband",
10+
"feather",
11+
"no",
12+
)
13+
DEFAULT_BLENDER = "multiband"
1014
DEFAULT_BLEND_STRENGTH = 5
1115

12-
def __init__(self, blender_type=DEFAULT_BLENDER,
13-
blend_strength=DEFAULT_BLEND_STRENGTH):
16+
def __init__(
17+
self, blender_type=DEFAULT_BLENDER, blend_strength=DEFAULT_BLEND_STRENGTH
18+
):
1419
self.blender_type = blender_type
1520
self.blend_strength = blend_strength
1621
self.blender = None
1722

1823
def prepare(self, corners, sizes):
1924
dst_sz = cv.detail.resultRoi(corners=corners, sizes=sizes)
20-
blend_width = (np.sqrt(dst_sz[2] * dst_sz[3]) *
21-
self.blend_strength / 100)
25+
blend_width = np.sqrt(dst_sz[2] * dst_sz[3]) * self.blend_strength / 100
2226

23-
if self.blender_type == 'no' or blend_width < 1:
24-
self.blender = cv.detail.Blender_createDefault(
25-
cv.detail.Blender_NO
26-
)
27+
if self.blender_type == "no" or blend_width < 1:
28+
self.blender = cv.detail.Blender_createDefault(cv.detail.Blender_NO)
2729

2830
elif self.blender_type == "multiband":
2931
self.blender = cv.detail_MultiBandBlender()
30-
self.blender.setNumBands(int((np.log(blend_width) /
31-
np.log(2.) - 1.)))
32+
self.blender.setNumBands(int((np.log(blend_width) / np.log(2.0) - 1.0)))
3233

3334
elif self.blender_type == "feather":
3435
self.blender = cv.detail_FeatherBlender()
35-
self.blender.setSharpness(1. / blend_width)
36+
self.blender.setSharpness(1.0 / blend_width)
3637

3738
self.blender.prepare(dst_sz)
3839

stitching/camera_adjuster.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,47 @@
11
from collections import OrderedDict
2+
23
import cv2 as cv
34
import numpy as np
45

56
from .stitching_error import StitchingError
67

78

89
class CameraAdjuster:
9-
"""https://docs.opencv.org/4.x/d5/d56/classcv_1_1detail_1_1BundleAdjusterBase.html""" # noqa
10+
"""https://docs.opencv.org/4.x/d5/d56/classcv_1_1detail_1_1BundleAdjusterBase.html""" # noqa: E501
1011

1112
CAMERA_ADJUSTER_CHOICES = OrderedDict()
12-
CAMERA_ADJUSTER_CHOICES['ray'] = cv.detail_BundleAdjusterRay
13-
CAMERA_ADJUSTER_CHOICES['reproj'] = cv.detail_BundleAdjusterReproj
14-
CAMERA_ADJUSTER_CHOICES['affine'] = cv.detail_BundleAdjusterAffinePartial
15-
CAMERA_ADJUSTER_CHOICES['no'] = cv.detail_NoBundleAdjuster
13+
CAMERA_ADJUSTER_CHOICES["ray"] = cv.detail_BundleAdjusterRay
14+
CAMERA_ADJUSTER_CHOICES["reproj"] = cv.detail_BundleAdjusterReproj
15+
CAMERA_ADJUSTER_CHOICES["affine"] = cv.detail_BundleAdjusterAffinePartial
16+
CAMERA_ADJUSTER_CHOICES["no"] = cv.detail_NoBundleAdjuster
1617

1718
DEFAULT_CAMERA_ADJUSTER = list(CAMERA_ADJUSTER_CHOICES.keys())[0]
1819
DEFAULT_REFINEMENT_MASK = "xxxxx"
1920

20-
def __init__(self,
21-
adjuster=DEFAULT_CAMERA_ADJUSTER,
22-
refinement_mask=DEFAULT_REFINEMENT_MASK):
21+
def __init__(
22+
self, adjuster=DEFAULT_CAMERA_ADJUSTER, refinement_mask=DEFAULT_REFINEMENT_MASK
23+
):
2324

2425
self.adjuster = CameraAdjuster.CAMERA_ADJUSTER_CHOICES[adjuster]()
2526
self.set_refinement_mask(refinement_mask)
2627
self.adjuster.setConfThresh(1)
2728

2829
def set_refinement_mask(self, refinement_mask):
2930
mask_matrix = np.zeros((3, 3), np.uint8)
30-
if refinement_mask[0] == 'x':
31+
if refinement_mask[0] == "x":
3132
mask_matrix[0, 0] = 1
32-
if refinement_mask[1] == 'x':
33+
if refinement_mask[1] == "x":
3334
mask_matrix[0, 1] = 1
34-
if refinement_mask[2] == 'x':
35+
if refinement_mask[2] == "x":
3536
mask_matrix[0, 2] = 1
36-
if refinement_mask[3] == 'x':
37+
if refinement_mask[3] == "x":
3738
mask_matrix[1, 1] = 1
38-
if refinement_mask[4] == 'x':
39+
if refinement_mask[4] == "x":
3940
mask_matrix[1, 2] = 1
4041
self.adjuster.setRefinementMask(mask_matrix)
4142

4243
def adjust(self, features, pairwise_matches, estimated_cameras):
43-
b, cameras = self.adjuster.apply(features,
44-
pairwise_matches,
45-
estimated_cameras)
44+
b, cameras = self.adjuster.apply(features, pairwise_matches, estimated_cameras)
4645
if not b:
4746
raise StitchingError("Camera parameters adjusting failed.")
4847

stitching/camera_estimator.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
from collections import OrderedDict
2+
23
import cv2 as cv
34
import numpy as np
45

56
from .stitching_error import StitchingError
67

78

89
class CameraEstimator:
9-
"""https://docs.opencv.org/4.x/df/d15/classcv_1_1detail_1_1Estimator.html""" # noqa
10+
"""https://docs.opencv.org/4.x/df/d15/classcv_1_1detail_1_1Estimator.html"""
1011

1112
CAMERA_ESTIMATOR_CHOICES = OrderedDict()
12-
CAMERA_ESTIMATOR_CHOICES['homography'] = cv.detail_HomographyBasedEstimator
13-
CAMERA_ESTIMATOR_CHOICES['affine'] = cv.detail_AffineBasedEstimator
13+
CAMERA_ESTIMATOR_CHOICES["homography"] = cv.detail_HomographyBasedEstimator
14+
CAMERA_ESTIMATOR_CHOICES["affine"] = cv.detail_AffineBasedEstimator
1415

1516
DEFAULT_CAMERA_ESTIMATOR = list(CAMERA_ESTIMATOR_CHOICES.keys())[0]
1617

1718
def __init__(self, estimator=DEFAULT_CAMERA_ESTIMATOR, **kwargs):
18-
self.estimator = CameraEstimator.CAMERA_ESTIMATOR_CHOICES[estimator](
19-
**kwargs
20-
)
19+
self.estimator = CameraEstimator.CAMERA_ESTIMATOR_CHOICES[estimator](**kwargs)
2120

2221
def estimate(self, features, pairwise_matches):
2322
b, cameras = self.estimator.apply(features, pairwise_matches, None)

stitching/camera_wave_corrector.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
from collections import OrderedDict
2+
23
import cv2 as cv
34
import numpy as np
45

56

67
class WaveCorrector:
7-
"""https://docs.opencv.org/4.x/d7/d74/group__stitching__rotation.html#ga8faf9588aebd5aeb6f8c649c82beb1fb""" # noqa
8+
"""https://docs.opencv.org/4.x/d7/d74/group__stitching__rotation.html#ga8faf9588aebd5aeb6f8c649c82beb1fb""" # noqa: E501
89

910
WAVE_CORRECT_CHOICES = OrderedDict()
10-
WAVE_CORRECT_CHOICES['horiz'] = cv.detail.WAVE_CORRECT_HORIZ
11-
WAVE_CORRECT_CHOICES['vert'] = cv.detail.WAVE_CORRECT_VERT
12-
WAVE_CORRECT_CHOICES['auto'] = cv.detail.WAVE_CORRECT_AUTO
13-
WAVE_CORRECT_CHOICES['no'] = None
11+
WAVE_CORRECT_CHOICES["horiz"] = cv.detail.WAVE_CORRECT_HORIZ
12+
WAVE_CORRECT_CHOICES["vert"] = cv.detail.WAVE_CORRECT_VERT
13+
WAVE_CORRECT_CHOICES["auto"] = cv.detail.WAVE_CORRECT_AUTO
14+
WAVE_CORRECT_CHOICES["no"] = None
1415

1516
DEFAULT_WAVE_CORRECTION = list(WAVE_CORRECT_CHOICES.keys())[0]
1617

1718
def __init__(self, wave_correct_kind=DEFAULT_WAVE_CORRECTION):
18-
self.wave_correct_kind = WaveCorrector.WAVE_CORRECT_CHOICES[
19-
wave_correct_kind
20-
]
19+
self.wave_correct_kind = WaveCorrector.WAVE_CORRECT_CHOICES[wave_correct_kind]
2120

2221
def correct(self, cameras):
2322
if self.wave_correct_kind is not None:

stitching/cli/stitch.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"--affine",
3030
action="store_true",
3131
help="Overwrites multiple parameters to optimize the stitching for "
32-
"scans and images captured by specialized devices."
32+
"scans and images captured by specialized devices.",
3333
)
3434
parser.add_argument(
3535
"--medium_megapix",
@@ -267,12 +267,14 @@ def main():
267267

268268
# Set parameters for 'affine' mode
269269
affine_mode = args_dict.pop("affine")
270-
affine_settings = {"estimator": "affine",
271-
"wave_correct_kind": "no",
272-
"matcher_type": "affine",
273-
"adjuster": "affine",
274-
"warper_type": "affine",
275-
"compensator": "no"}
270+
affine_settings = {
271+
"estimator": "affine",
272+
"wave_correct_kind": "no",
273+
"matcher_type": "affine",
274+
"adjuster": "affine",
275+
"warper_type": "affine",
276+
"compensator": "no",
277+
}
276278
if affine_mode:
277279
args_dict.update(affine_settings)
278280

stitching/cropper.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from collections import namedtuple
2+
23
import cv2 as cv
34
import numpy as np
45

56
from .blender import Blender
67
from .stitching_error import StitchingError
78

89

9-
class Rectangle(namedtuple('Rectangle', 'x y width height')):
10+
class Rectangle(namedtuple("Rectangle", "x y width height")):
1011
__slots__ = ()
1112

1213
@property
@@ -30,13 +31,13 @@ def y2(self):
3031
return self.y + self.height
3132

3233
def times(self, x):
33-
return Rectangle(*(int(round(i*x)) for i in self))
34+
return Rectangle(*(int(round(i * x)) for i in self))
3435

3536
def draw_on(self, img, color=(0, 0, 255), size=1):
3637
if len(img.shape) == 2:
3738
img = cv.cvtColor(img, cv.COLOR_GRAY2RGB)
3839
start_point = (self.x, self.y)
39-
end_point = (self.x2-1, self.y2-1)
40+
end_point = (self.x2 - 1, self.y2 - 1)
4041
cv.rectangle(img, start_point, end_point, color, size)
4142
return img
4243

@@ -56,10 +57,10 @@ def prepare(self, imgs, masks, corners, sizes):
5657
lir = self.estimate_largest_interior_rectangle(mask)
5758
corners = self.get_zero_center_corners(corners)
5859
rectangles = self.get_rectangles(corners, sizes)
59-
self.overlapping_rectangles = self.get_overlaps(
60-
rectangles, lir)
60+
self.overlapping_rectangles = self.get_overlaps(rectangles, lir)
6161
self.intersection_rectangles = self.get_intersections(
62-
rectangles, self.overlapping_rectangles)
62+
rectangles, self.overlapping_rectangles
63+
)
6364

6465
def crop_images(self, imgs, aspect=1):
6566
for idx, img in enumerate(imgs):
@@ -75,8 +76,7 @@ def crop_img(self, img, idx, aspect=1):
7576

7677
def crop_rois(self, corners, sizes, aspect=1):
7778
if self.do_crop:
78-
scaled_overlaps = \
79-
[r.times(aspect) for r in self.overlapping_rectangles]
79+
scaled_overlaps = [r.times(aspect) for r in self.overlapping_rectangles]
8080
cropped_corners = [r.corner for r in scaled_overlaps]
8181
cropped_corners = self.get_zero_center_corners(cropped_corners)
8282
cropped_sizes = [r.size for r in scaled_overlaps]
@@ -93,8 +93,7 @@ def estimate_largest_interior_rectangle(self, mask):
9393
# is explicitely desired (needs some time to compile at the first run!)
9494
import largestinteriorrectangle
9595

96-
contours, hierarchy = \
97-
cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
96+
contours, hierarchy = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
9897
if not hierarchy.shape == (1, 1, 4) or not np.all(hierarchy == -1):
9998
raise StitchingError("Invalid Contour. Try without cropping.")
10099
contour = contours[0][:, 0, :]
@@ -129,12 +128,14 @@ def get_overlap(rectangle1, rectangle2):
129128
y2 = min(rectangle1.y2, rectangle2.y2)
130129
if x2 < x1 or y2 < y1:
131130
raise StitchingError("Rectangles do not overlap!")
132-
return Rectangle(x1, y1, x2-x1, y2-y1)
131+
return Rectangle(x1, y1, x2 - x1, y2 - y1)
133132

134133
@staticmethod
135134
def get_intersections(rectangles, overlapping_rectangles):
136-
return [Cropper.get_intersection(r, overlap_r) for r, overlap_r
137-
in zip(rectangles, overlapping_rectangles)]
135+
return [
136+
Cropper.get_intersection(r, overlap_r)
137+
for r, overlap_r in zip(rectangles, overlapping_rectangles)
138+
]
138139

139140
@staticmethod
140141
def get_intersection(rectangle, overlapping_rectangle):
@@ -146,4 +147,4 @@ def get_intersection(rectangle, overlapping_rectangle):
146147

147148
@staticmethod
148149
def crop_rectangle(img, rectangle):
149-
return img[rectangle.y:rectangle.y2, rectangle.x:rectangle.x2]
150+
return img[rectangle.y : rectangle.y2, rectangle.x : rectangle.x2]

0 commit comments

Comments
 (0)