Skip to content

Add Chinese comments to all functions and key code #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions stitching/blender.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ class Blender:
def __init__(
self, blender_type=DEFAULT_BLENDER, blend_strength=DEFAULT_BLEND_STRENGTH
):
"""
初始化Blender类
:param blender_type: 混合器类型
:param blend_strength: 混合强度
"""
self.blender_type = blender_type
self.blend_strength = blend_strength
self.blender = None

def prepare(self, corners, sizes):
"""
准备混合器
:param corners: 图像的角点
:param sizes: 图像的尺寸
"""
dst_sz = cv.detail.resultRoi(corners=corners, sizes=sizes)
blend_width = np.sqrt(dst_sz[2] * dst_sz[3]) * self.blend_strength / 100

Expand All @@ -38,9 +48,19 @@ def prepare(self, corners, sizes):
self.blender.prepare(dst_sz)

def feed(self, img, mask, corner):
"""
向混合器中添加图像
:param img: 图像
:param mask: 掩码
:param corner: 角点
"""
self.blender.feed(cv.UMat(img.astype(np.int16)), mask, corner)

def blend(self):
"""
混合图像
:return: 混合后的图像和掩码
"""
result = None
result_mask = None
result, result_mask = self.blender.blend(result, result_mask)
Expand All @@ -49,6 +69,14 @@ def blend(self):

@classmethod
def create_panorama(cls, imgs, masks, corners, sizes):
"""
创建全景图
:param imgs: 图像列表
:param masks: 掩码列表
:param corners: 角点列表
:param sizes: 尺寸列表
:return: 全景图和掩码
"""
blender = cls("no")
blender.prepare(corners, sizes)
for img, mask, corner in zip(imgs, masks, corners):
Expand Down
17 changes: 17 additions & 0 deletions stitching/camera_adjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,21 @@ def __init__(
refinement_mask=DEFAULT_REFINEMENT_MASK,
confidence_threshold=1.0,
):
"""
初始化CameraAdjuster类
:param adjuster: 调整器类型
:param refinement_mask: 精细化掩码
:param confidence_threshold: 置信度阈值
"""
self.adjuster = CameraAdjuster.CAMERA_ADJUSTER_CHOICES[adjuster]()
self.set_refinement_mask(refinement_mask)
self.adjuster.setConfThresh(confidence_threshold)

def set_refinement_mask(self, refinement_mask):
"""
设置精细化掩码
:param refinement_mask: 精细化掩码
"""
mask_matrix = np.zeros((3, 3), np.uint8)
if refinement_mask[0] == "x":
mask_matrix[0, 0] = 1
Expand All @@ -43,6 +53,13 @@ def set_refinement_mask(self, refinement_mask):
self.adjuster.setRefinementMask(mask_matrix)

def adjust(self, features, pairwise_matches, estimated_cameras):
"""
调整相机参数
:param features: 特征点
:param pairwise_matches: 成对匹配
:param estimated_cameras: 估计的相机参数
:return: 调整后的相机参数
"""
b, cameras = self.adjuster.apply(features, pairwise_matches, estimated_cameras)
if not b:
raise StitchingError("Camera parameters adjusting failed.")
Expand Down
11 changes: 11 additions & 0 deletions stitching/camera_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,20 @@ class CameraEstimator:
DEFAULT_CAMERA_ESTIMATOR = list(CAMERA_ESTIMATOR_CHOICES.keys())[0]

def __init__(self, estimator=DEFAULT_CAMERA_ESTIMATOR, **kwargs):
"""
初始化CameraEstimator类
:param estimator: 估计器类型
:param kwargs: 其他参数
"""
self.estimator = CameraEstimator.CAMERA_ESTIMATOR_CHOICES[estimator](**kwargs)

def estimate(self, features, pairwise_matches):
"""
估计相机参数
:param features: 特征点
:param pairwise_matches: 成对匹配
:return: 估计的相机参数
"""
b, cameras = self.estimator.apply(features, pairwise_matches, None)
if not b:
raise StitchingError("Homography estimation failed.")
Expand Down
9 changes: 9 additions & 0 deletions stitching/camera_wave_corrector.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ class WaveCorrector:
DEFAULT_WAVE_CORRECTION = list(WAVE_CORRECT_CHOICES.keys())[0]

def __init__(self, wave_correct_kind=DEFAULT_WAVE_CORRECTION):
"""
初始化WaveCorrector类
:param wave_correct_kind: 波形校正类型
"""
self.wave_correct_kind = WaveCorrector.WAVE_CORRECT_CHOICES[wave_correct_kind]

def correct(self, cameras):
"""
校正相机波形
:param cameras: 相机参数列表
:return: 校正后的相机参数列表
"""
if self.wave_correct_kind is not None:
rmats = [np.copy(cam.R) for cam in cameras]
rmats = cv.detail.waveCorrect(rmats, self.wave_correct_kind)
Expand Down
85 changes: 85 additions & 0 deletions stitching/cropper.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,22 @@ class Cropper:
DEFAULT_CROP = True

def __init__(self, crop=DEFAULT_CROP):
"""
初始化Cropper类
:param crop: 是否进行裁剪
"""
self.do_crop = crop
self.overlapping_rectangles = []
self.cropping_rectangles = []

def prepare(self, imgs, masks, corners, sizes):
"""
准备裁剪器
:param imgs: 图像列表
:param masks: 掩码列表
:param corners: 角点列表
:param sizes: 尺寸列表
"""
if self.do_crop:
mask = self.estimate_panorama_mask(imgs, masks, corners, sizes)
lir = self.estimate_largest_interior_rectangle(mask)
Expand All @@ -62,10 +73,23 @@ def prepare(self, imgs, masks, corners, sizes):
)

def crop_images(self, imgs, aspect=1):
"""
裁剪图像
:param imgs: 图像列表
:param aspect: 缩放比例
:return: 裁剪后的图像生成器
"""
for idx, img in enumerate(imgs):
yield self.crop_img(img, idx, aspect)

def crop_img(self, img, idx, aspect=1):
"""
裁剪单张图像
:param img: 图像
:param idx: 图像索引
:param aspect: 缩放比例
:return: 裁剪后的图像
"""
if self.do_crop:
intersection_rect = self.intersection_rectangles[idx]
scaled_intersection_rect = intersection_rect.times(aspect)
Expand All @@ -74,6 +98,13 @@ def crop_img(self, img, idx, aspect=1):
return img

def crop_rois(self, corners, sizes, aspect=1):
"""
裁剪感兴趣区域(ROI)
:param corners: 角点列表
:param sizes: 尺寸列表
:param aspect: 缩放比例
:return: 裁剪后的角点和尺寸
"""
if self.do_crop:
scaled_overlaps = [r.times(aspect) for r in self.overlapping_rectangles]
cropped_corners = [r.corner for r in scaled_overlaps]
Expand All @@ -84,10 +115,23 @@ def crop_rois(self, corners, sizes, aspect=1):

@staticmethod
def estimate_panorama_mask(imgs, masks, corners, sizes):
"""
估计全景图掩码
:param imgs: 图像列表
:param masks: 掩码列表
:param corners: 角点列表
:param sizes: 尺寸列表
:return: 全景图掩码
"""
_, mask = Blender.create_panorama(imgs, masks, corners, sizes)
return mask

def estimate_largest_interior_rectangle(self, mask):
"""
估计最大内部矩形
:param mask: 掩码
:return: 最大内部矩形
"""
# largestinteriorrectangle is only imported if cropping
# is explicitly desired (needs some time to compile at the first run!)
import largestinteriorrectangle
Expand All @@ -105,12 +149,23 @@ def estimate_largest_interior_rectangle(self, mask):

@staticmethod
def get_zero_center_corners(corners):
"""
获取以零为中心的角点
:param corners: 角点列表
:return: 以零为中心的角点列表
"""
min_corner_x = min([corner[0] for corner in corners])
min_corner_y = min([corner[1] for corner in corners])
return [(x - min_corner_x, y - min_corner_y) for x, y in corners]

@staticmethod
def get_rectangles(corners, sizes):
"""
获取矩形列表
:param corners: 角点列表
:param sizes: 尺寸列表
:return: 矩形列表
"""
rectangles = []
for corner, size in zip(corners, sizes):
rectangle = Rectangle(*corner, *size)
Expand All @@ -119,10 +174,22 @@ def get_rectangles(corners, sizes):

@staticmethod
def get_overlaps(rectangles, lir):
"""
获取重叠矩形列表
:param rectangles: 矩形列表
:param lir: 最大内部矩形
:return: 重叠矩形列表
"""
return [Cropper.get_overlap(r, lir) for r in rectangles]

@staticmethod
def get_overlap(rectangle1, rectangle2):
"""
获取两个矩形的重叠部分
:param rectangle1: 矩形1
:param rectangle2: 矩形2
:return: 重叠部分矩形
"""
x1 = max(rectangle1.x, rectangle2.x)
y1 = max(rectangle1.y, rectangle2.y)
x2 = min(rectangle1.x2, rectangle2.x2)
Expand All @@ -133,13 +200,25 @@ def get_overlap(rectangle1, rectangle2):

@staticmethod
def get_intersections(rectangles, overlapping_rectangles):
"""
获取矩形与重叠矩形的交集
:param rectangles: 矩形列表
:param overlapping_rectangles: 重叠矩形列表
:return: 交集矩形列表
"""
return [
Cropper.get_intersection(r, overlap_r)
for r, overlap_r in zip(rectangles, overlapping_rectangles)
]

@staticmethod
def get_intersection(rectangle, overlapping_rectangle):
"""
获取矩形与重叠矩形的交集
:param rectangle: 矩形
:param overlapping_rectangle: 重叠矩形
:return: 交集矩形
"""
x = abs(overlapping_rectangle.x - rectangle.x)
y = abs(overlapping_rectangle.y - rectangle.y)
width = overlapping_rectangle.width
Expand All @@ -148,4 +227,10 @@ def get_intersection(rectangle, overlapping_rectangle):

@staticmethod
def crop_rectangle(img, rectangle):
"""
裁剪矩形区域
:param img: 图像
:param rectangle: 矩形区域
:return: 裁剪后的图像
"""
return img[rectangle.y : rectangle.y2, rectangle.x : rectangle.x2]
17 changes: 15 additions & 2 deletions stitching/exposure_error_compensator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def __init__(
nr_feeds=DEFAULT_NR_FEEDS,
block_size=DEFAULT_BLOCK_SIZE,
):
"""
初始化ExposureErrorCompensator类
:param compensator: 曝光补偿器类型
:param nr_feeds: 曝光补偿器的数量
:param block_size: 块大小
"""
if compensator == "channel":
self.compensator = cv.detail_ChannelsCompensator(nr_feeds)
elif compensator == "channel_blocks":
Expand All @@ -37,9 +43,16 @@ def __init__(
)

def feed(self, *args):
"""https://docs.opencv.org/4.x/d2/d37/classcv_1_1detail_1_1ExposureCompensator.html#ae6b0cc69a7bc53818ddea53eddb6bdba""" # noqa
"""
向曝光补偿器提供数据
:param args: 数据参数
"""
self.compensator.feed(*args)

def apply(self, *args):
"""https://docs.opencv.org/4.x/d2/d37/classcv_1_1detail_1_1ExposureCompensator.html#a473eaf1e585804c08d77c91e004f93aa""" # noqa
"""
应用曝光补偿
:param args: 数据参数
:return: 曝光补偿后的结果
"""
return self.compensator.apply(*args)
30 changes: 30 additions & 0 deletions stitching/feature_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,38 @@ class FeatureDetector:
DEFAULT_DETECTOR = list(DETECTOR_CHOICES.keys())[0]

def __init__(self, detector=DEFAULT_DETECTOR, **kwargs):
"""
初始化FeatureDetector类
:param detector: 特征检测器类型
:param kwargs: 其他参数
"""
self.detector = FeatureDetector.DETECTOR_CHOICES[detector](**kwargs)

def detect_features(self, img, *args, **kwargs):
"""
检测图像中的特征
:param img: 图像
:param args: 其他参数
:param kwargs: 其他参数
:return: 检测到的特征
"""
return cv.detail.computeImageFeatures2(self.detector, img, *args, **kwargs)

def detect(self, imgs):
"""
检测图像列表中的特征
:param imgs: 图像列表
:return: 检测到的特征列表
"""
return [self.detect_features(img) for img in imgs]

def detect_with_masks(self, imgs, masks):
"""
使用掩码检测图像列表中的特征
:param imgs: 图像列表
:param masks: 掩码列表
:return: 检测到的特征列表
"""
features = []
for idx, (img, mask) in enumerate(zip(imgs, masks)):
assert len(img.shape) == 3 and len(mask.shape) == 2
Expand All @@ -43,6 +66,13 @@ def detect_with_masks(self, imgs, masks):

@staticmethod
def draw_keypoints(img, features, **kwargs):
"""
在图像上绘制特征点
:param img: 图像
:param features: 特征点
:param kwargs: 其他参数
:return: 绘制了特征点的图像
"""
kwargs.setdefault("color", (0, 255, 0))
keypoints = features.getKeypoints()
return cv.drawKeypoints(img, keypoints, None, **kwargs)
Loading