opencv 轮廓合并

opencv 轮廓合并

本文将指导你将多个轮廓合并为一个轮廓,主要操作逻辑为:寻找各轮廓之间的最短距离,并按最短距离对应的点进行轮廓拼接,以达到轮廓合并的效果。
以下为运算处理的结果
示例对照

实现步骤

opencv的导入

pip install opencv-python

或者

pip install opencv-python-headless

加载图像与轮廓

加载包括多个轮廓的图像

check_piece_image = cv2.imread("check_piece_image.jpg")
cv2.imshow("check_piece_image", check_piece_image)

check_piece_image
做灰度图处理

check_piece_gray_image = cv2.cvtColor(check_piece_image, cv2.COLOR_BGR2GRAY)
cv2.imshow("check_piece_gray_image", check_piece_gray_image)

check_piece_gray_image
因为图像是白底黑字,则需要按位取反,如果你们不是,那么这步可以忽略

check_piece_bitwise_not_image = cv2.bitwise_not(check_piece_gray_image)
cv2.imshow("check_piece_bitwise_not_image", check_piece_bitwise_not_image)

check_piece_bitwise_not_image
阙值化处理

# _, check_piece_thresh_image = cv2.threshold(check_piece_gray_image, 100, 255, cv2.THRESH_OTSU)
_, check_piece_thresh_image = cv2.threshold(check_piece_bitwise_not_image, 100, 255, cv2.THRESH_OTSU)
cv2.imshow("check_piece_thresh_image", check_piece_thresh_image)

check_piece_thresh_image
寻找所有的轮廓

check_piece_contours, _ = cv2.findContours(check_piece_thresh_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
check_piece_contours_image = check_piece_image.copy()
cv2.drawContours(check_piece_contours_image, check_piece_contours, -1, (0, 255, 0), 2)
cv2.imshow("check_piece_contours_image", check_piece_contours_image)

check_piece_contours_image

对轮廓进行合并

合并轮廓 寻找最小距离并合并为一个轮廓

new_merged_check_piece_contours = merge_contours(check_piece_contours)
check_piece_contours_image_by_merged = check_piece_image.copy()
cv2.drawContours(check_piece_contours_image_by_merged, new_merged_check_piece_contours, -1, (0, 255, 0), 2)
cv2.imshow("check_piece_contours_image_by_merged", check_piece_contours_image_by_merged)

核心方法

def merge_contours(contours: typing.Sequence[cv2.typing.MatLike]) -> typing.Sequence[cv2.typing.MatLike]:
    """合并轮廓 寻找最小距离并合并为一个轮廓

    Args:
        contours (typing.Sequence[cv2.typing.MatLike]): 轮廓

    Returns:
        typing.Sequence[cv2.typing.MatLike]: 合并后的轮廓
    """
    contours = list(contours).copy()
    if len(contours) <= 1:
        return contours
    min_distance = float('inf')
    closest_i = None
    closest_j = None
    new_contour = None
    for i in range(len(contours)):
        for j in range(i + 1, len(contours)):
            candidate_contour, distance = merge_two_contour_to_one(contours[i], contours[j])
            if distance < min_distance:
                new_contour, min_distance, closest_i, closest_j = candidate_contour, distance, i, j
    contours[closest_i] = new_contour
    del contours[closest_j]
    return merge_contours(contours)

def merge_two_contour_to_one(contour1: cv2.typing.MatLike, contour2: cv2.typing.MatLike) -> tuple[cv2.typing.MatLike, float]:
    min_distance, closest_i, closest_j = float('inf'), None, None
    for i, point1 in enumerate(contour1):
        for j, point2 in enumerate(contour2):
            distance = np.linalg.norm(point1[0] - point2[0], 2, None, False)
            if distance < min_distance:
                min_distance, closest_i, closest_j = distance, i, j
    rearranged_contour1 = np.vstack((contour1[closest_i:], contour1[:closest_i]))
    rearranged_contour2 = np.vstack((contour2[closest_j:], contour2[:closest_j]))
    return np.vstack((rearranged_contour1, rearranged_contour2)), min_distance

check_piece_contours_image_by_merged

全部代码

所有代码以及所有过程截图

import cv2
from cv2.typing import MatLike
import typing
import numpy as np

def merge_contours(contours: typing.Sequence[cv2.typing.MatLike]) -> typing.Sequence[cv2.typing.MatLike]:
    """合并轮廓 寻找最小距离并合并为一个轮廓

    Args:
        contours (typing.Sequence[cv2.typing.MatLike]): 轮廓

    Returns:
        typing.Sequence[cv2.typing.MatLike]: 合并后的轮廓
    """
    contours = list(contours).copy()
    if len(contours) <= 1:
        return contours
    min_distance = float('inf')
    closest_i = None
    closest_j = None
    new_contour = None
    for i in range(len(contours)):
        for j in range(i + 1, len(contours)):
            candidate_contour, distance = merge_two_contour_to_one(contours[i], contours[j])
            if distance < min_distance:
                new_contour, min_distance, closest_i, closest_j = candidate_contour, distance, i, j
    contours[closest_i] = new_contour
    del contours[closest_j]
    return merge_contours(contours)

def merge_two_contour_to_one(contour1: cv2.typing.MatLike, contour2: cv2.typing.MatLike) -> tuple[cv2.typing.MatLike, float]:
    min_distance, closest_i, closest_j = float('inf'), None, None
    for i, point1 in enumerate(contour1):
        for j, point2 in enumerate(contour2):
            distance = np.linalg.norm(point1[0] - point2[0], 2, None, False)
            if distance < min_distance:
                min_distance, closest_i, closest_j = distance, i, j
    rearranged_contour1 = np.vstack((contour1[closest_i:], contour1[:closest_i]))
    rearranged_contour2 = np.vstack((contour2[closest_j:], contour2[:closest_j]))
    return np.vstack((rearranged_contour1, rearranged_contour2)), min_distance

if __name__ == "__main__":
    check_piece_image = cv2.imread("D:/wzj/merge-contours/check_piece_image.jpg")
    cv2.imshow("check_piece_image", check_piece_image)

    check_piece_gray_image = cv2.cvtColor(check_piece_image, cv2.COLOR_BGR2GRAY)
    cv2.imshow("check_piece_gray_image", check_piece_gray_image)

    check_piece_bitwise_not_image = cv2.bitwise_not(check_piece_gray_image)
    cv2.imshow("check_piece_bitwise_not_image", check_piece_bitwise_not_image)

    # _, check_piece_thresh_image = cv2.threshold(check_piece_gray_image, 100, 255, cv2.THRESH_OTSU)
    _, check_piece_thresh_image = cv2.threshold(check_piece_bitwise_not_image, 100, 255, cv2.THRESH_OTSU)
    cv2.imshow("check_piece_thresh_image", check_piece_thresh_image)

    check_piece_contours, _ = cv2.findContours(check_piece_thresh_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    check_piece_contours_image = check_piece_image.copy()
    cv2.drawContours(check_piece_contours_image, check_piece_contours, -1, (0, 255, 0), 2)
    cv2.imshow("check_piece_contours_image", check_piece_contours_image)

    new_merged_check_piece_contours = merge_contours(check_piece_contours)
    check_piece_contours_image_by_merged = check_piece_image.copy()
    cv2.drawContours(check_piece_contours_image_by_merged, new_merged_check_piece_contours, -1, (0, 255, 0), 2)
    cv2.imshow("check_piece_contours_image_by_merged", check_piece_contours_image_by_merged)

    cv2.waitKey(0)

example_result

题外话

轮廓的合并,我有尝试其他,例如最初直接堆叠,发现效果并不好,而且无法控制轮廓的顺序;后面又考虑近似轮廓以及凸包,但关于它的一些扩展并不理想就是了;如果你们对轮廓合并有些其他的想法,可以在下面的评论中指出,欢迎指导
这里贴一些其他轮廓处理的截图

直接堆叠
all_check_piece_contours = np.vstack((check_piece_contours))
check_piece_contours_image_0 = check_piece_image.copy()
cv2.drawContours(check_piece_contours_image_0, [all_check_piece_contours], -1, (0, 255, 0), 2)
cv2.imshow("check_piece_contours_image_0", check_piece_contours_image_0)

check_piece_contours_image_0

近似轮廓
epsilon = 0.01 * cv2.arcLength(all_check_piece_contours, True)
approx_contour = cv2.approxPolyDP(all_check_piece_contours, epsilon, True)
check_piece_contours_image_1 = check_piece_image.copy()
cv2.drawContours(check_piece_contours_image_1, [approx_contour], -1, (0, 255, 0), 2)
cv2.imshow("check_piece_contours_image_1", check_piece_contours_image_1)

check_piece_contours_image_1

凸包处理的轮廓
hull = cv2.convexHull(all_check_piece_contours)
check_piece_contours_image_2 = check_piece_image.copy()
cv2.drawContours(check_piece_contours_image_2, [hull], -1, (0, 255, 0), 2)
cv2.imshow("check_piece_contours_image_2", check_piece_contours_image_2)

check_piece_contours_image_2

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值