Program - 图像去除周围黑色区域和获得最大内接矩形

本文介绍了一种通过洪水漫灌算法去除图像旋转矫正后的黑色边缘的方法,以获取最大内接矩形区域,便于后续的图像训练与预测。文章详细展示了如何使用OpenCV进行图像处理,并通过轮廓检测提取有效区域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法的目标是这样的,目前有一批图像,是旋转矫正之后的图像,旋转之后,周围的空白,使用黑色填充。现在需要一批,矫正角度之后的图像,用于训练和预测,图像的旋转角度。因此,需要提取这些图像的最大内接矩形,去掉周围的黑色区域。

原始图像:
原图像
目标效果,即只截取中心区域:
输出图像
获取图像的高和宽

h, w, _ = img_bgr.shape
img_copy = copy.copy(img_bgr)

使用洪水漫灌算法,避免周围出现一些干扰点,保证4个角的都是连通域,处理之后,Mask的四个角的值是1,其他地方是0。

def show_img_bgr(img_bgr, save_name=None):
    """
    展示BGR彩色图
    """
    import cv2
    import matplotlib.pyplot as plt

    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    plt.imshow(img_rgb)
    plt.show()

    if save_name:
        print('[Info] 存储图像: {}'.format(save_name))
        plt.imsave(save_name, img_rgb)
        
mask = np.zeros((h + 2, w + 2), np.uint8)  # Mask
cv2.floodFill(img_copy, mask=mask, seedPoint=(0, 0), newVal=(0, 0, 255),
              loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
cv2.floodFill(img_copy, mask=mask, seedPoint=(w-1, 0), newVal=(0, 0, 255),
              loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
cv2.floodFill(img_copy, mask=mask, seedPoint=(0, h-1), newVal=(0, 0, 255),
              loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
cv2.floodFill(img_copy, mask=mask, seedPoint=(w-1, h-1), newVal=(0, 0, 255),
              loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
show_img_bgr(img_copy)

效果:
floodFill
反转Mask的值,0和1互换,因为中心区域才是我们关注的区域,使用np.where(),即可反转,即:

mask_inv = np.where(mask > 0.5, 0, 1).astype(np.uint8)  # 反转mask的0和1

因为图像周围可能会存在黑色区域,导致洪水漫灌之后,会出现其他异常区域,使用形态学的开区间,则可以去掉小的区域。

# 形态学Opening,过滤较小的非连通区域
kernel = np.ones((9, 9), np.uint8)
mask_inv = cv2.morphologyEx(mask_inv, cv2.MORPH_OPEN, kernel)
show_img_bgr(mask_inv * 255)

现在就是要从白色区域中,获得最大的内接矩形。
opening
获取关注区域的角点,只保留1个连通区域,如:

contours, hierarchy = cv2.findContours(mask_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c_points = np.squeeze(contours)  # 确保1个连通区域,多个连通区域会异常

边界图像:

边界图像

获取角点的X和Y坐标,如下:

# 获取点的全部X\Y坐标
x_list, y_list = [], []
for p in c_points:
    x_list.append(p[0])
    y_list.append(p[1])
x_list = np.asarray(x_list)
y_list = np.asarray(y_list)

根据XY的坐标,获取内接Box的值,即[x_min, y_min, x_max, y_max],如下:

# 从XY坐标中,提取区域的最大矩形区域,即[x_min, y_min, x_max, y_max]
x_min_idx = np.argmin(x_list)
y_1 = y_list[x_min_idx]
x_max_idx = np.argmax(x_list)
y_2 = y_list[x_max_idx]
y_max, y_min = max(y_1, y_2), min(y_1, y_2)

y_min_idx = np.argmin(y_list)
x_1 = x_list[y_min_idx]
y_max_idx = np.argmax(y_list)
x_2 = x_list[y_max_idx]
x_max, x_min = max(x_1, x_2), min(x_1, x_2)

根据Bounding Box获取中心区域的图像,如下:

def get_patch(img, box):
    """
    获取Img的Patch
    :param img: 图像
    :param box: [x_min, y_min, x_max, y_max]
    :return 图像块
    """
    h, w, _ = img.shape
    x_min = int(max(0, box[0]))
    y_min = int(max(0, box[1]))
    x_max = int(min(box[2], w))
    y_max = int(min(box[3], h))

    img_patch = img[y_min:y_max, x_min:x_max, :]
    return img_patch
    
img_patch = get_patch(img_bgr, [x_min, y_min, x_max, y_max])
show_img_bgr(img_patch)

结果如下:

output
最终实现,我们需要的效果。

全部源码:

def cut_img_without_margin(self, img_bgr):
    """
    去掉旋转图像四个角的黑色区域,保留最大的内接矩形
    :param img_bgr: bgr图像
    :return: 最大的内接矩形图像
    """
    from myutils.cv_utils import show_img_bgr
    show_img_bgr(img_bgr)

    h, w, _ = img_bgr.shape
    img_copy = copy.copy(img_bgr)

    # 从4个角,洪水漫灌,选择4个角的联通区域,
    mask = np.zeros((h + 2, w + 2), np.uint8)  # Mask
    cv2.floodFill(img_copy, mask=mask, seedPoint=(0, 0), newVal=(0, 0, 255),
                  loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
    cv2.floodFill(img_copy, mask=mask, seedPoint=(w-1, 0), newVal=(0, 0, 255),
                  loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
    cv2.floodFill(img_copy, mask=mask, seedPoint=(0, h-1), newVal=(0, 0, 255),
                  loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
    cv2.floodFill(img_copy, mask=mask, seedPoint=(w-1, h-1), newVal=(0, 0, 255),
                  loDiff=(5, 5, 5, 5), upDiff=(5, 5, 5, 5))
    show_img_bgr(img_copy)

    mask_inv = np.where(mask > 0.5, 0, 1).astype(np.uint8)  # 反转mask的0和1

    # 形态学Opening,过滤较小的非连通区域
    kernel = np.ones((9, 9), np.uint8)
    mask_inv = cv2.morphologyEx(mask_inv, cv2.MORPH_OPEN, kernel)
    show_img_bgr(mask_inv * 255)

    # 中心区域是1,其他位置是0
    contours, hierarchy = cv2.findContours(mask_inv, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    c_points = np.squeeze(contours)  # 确保1个连通区域,多个连通区域会异常

    # 测试连通区域
    cv2.drawContours(img_bgr, contours, -1, (0, 255, 0), 3)
    show_img_bgr(img_bgr)

    # 获取点的全部X\Y坐标
    x_list, y_list = [], []
    for p in c_points:
        x_list.append(p[0])
        y_list.append(p[1])
    x_list = np.asarray(x_list)
    y_list = np.asarray(y_list)

    # 从XY坐标中,提取区域的最大矩形区域,即[x_min, y_min, x_max, y_max]
    x_min_idx = np.argmin(x_list)
    y_1 = y_list[x_min_idx]
    x_max_idx = np.argmax(x_list)
    y_2 = y_list[x_max_idx]
    y_max, y_min = max(y_1, y_2), min(y_1, y_2)

    y_min_idx = np.argmin(y_list)
    x_1 = x_list[y_min_idx]
    y_max_idx = np.argmax(y_list)
    x_2 = x_list[y_max_idx]
    x_max, x_min = max(x_1, x_2), min(x_1, x_2)

    # 获取核心的中心区域
    img_patch = get_patch(img_bgr, [x_min, y_min, x_max, y_max])
    show_img_bgr(img_patch)

    # 写入输出图像
    img_out_path = os.path.join(DATA_DIR, 'out.jpg')
    cv2.imwrite(img_out_path, img_patch)

    print('[Info] 处理完成!')

    return img_patch

参考:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ManonLegrand

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值