使用OPENCV查找边界,提取出面积前top_n的图像部分,顺序粘贴到指定背景图像上合成单个照片

使用OPENCV查找边界,提取出面积前top_n的图像部分,顺序粘贴到指定背景图像上合成单个照片
注意,仅适用于简单边缘边界

import argparse
import sys

import cv2
import os
import numpy as np


def generate_random_smooth_black_image(height, width, base_color_random=10, noise_scale=30, k_size=8):
    r_value = np.random.randint(0, base_color_random)  # 红色通道的值
    g_value = np.random.randint(0, base_color_random)  # 绿色通道的值
    b_value = np.random.randint(0, base_color_random)  # 蓝色通道的值
    image = np.dstack((np.full((height, width), r_value, dtype=np.uint8),
                       np.full((height, width), g_value, dtype=np.uint8),
                       np.full((height, width), b_value, dtype=np.uint8)))

    # 生成随机的RGB噪声值(每个通道)
    noise_r = np.random.randint(-noise_scale, noise_scale + 1, (height, width), dtype=np.int16)
    noise_g = np.random.randint(-noise_scale, noise_scale + 1, (height, width), dtype=np.int16)
    noise_b = np.random.randint(-noise_scale, noise_scale + 1, (height, width), dtype=np.int16)

    # 将噪声添加到黑色图像上
    image_noisy = image.astype(np.int16) + np.dstack([noise_r, noise_g, noise_b])

    # 应用高斯滤波以平滑噪声
    smoothed_image = cv2.blur(image_noisy, (k_size, k_size))

    # 确保所有像素值都在0-255范围内
    smoothed_image = np.clip(smoothed_image, 0, 255).astype(np.uint8)

    return smoothed_image


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='请指定参数')
    parser.add_argument('--s', default='./', type=str, required=False,
                        help='source,需处理的文件目录,支持相对路径以及绝对路径,如未指定,则是当前目录')
    parser.add_argument('--t', default='./result', type=str, required=False,
                        help='target,输出的文件目录,支持相对路径以及绝对路径,会自动创建')
    parser.add_argument('--b', default=None, type=str, required=False,
                        help='background,背景图像,注意尺寸,默认是生成随机的连续黑背景')
    parser.add_argument('--sp', default=20, type=int, required=False, help='spacing,间距,单位像素')
    parser.add_argument('--x', default=450, type=int, required=False,
                        help='start_x,起始点,即被分割的图像的第一块区域的起始点x坐标')
    parser.add_argument('--y', default=100, type=int, required=False,
                        help='start_y,起始点,即被分割的图像的第一块区域的起始点y坐标')
    parser.add_argument('--top_n', default=3, type=int, required=False,
                        help='取前n个面积的边缘排列出来')
    parser.add_argument('--random_y', default=10, type=int, required=False, help='y的对齐随机参数')
    parser.add_argument('--base_color_random', default=10, type=int, required=False, help='背景底图的随机范围,作用于3通道')
    parser.add_argument('--noise_scale', default=20, type=int, required=False, help='背景图像的噪声强度,越大噪声越强')
    parser.add_argument('--k_size', default=8, type=int, required=False, help='背景平均模糊参数,越大越柔和,即背景图像的平均模糊核心大小')

    args = parser.parse_args()

    # 源目录和目标目录
    source_dir = args.s  # 包含待处理图像的目录
    target_dir = args.t  # 用于保存结果的目录
    top_n = args.top_n  # 用于保存结果的目录

    if not os.path.exists(source_dir):
        print("source_dir not exists")
        sys.exit(1)

    if args.b is not None and not os.path.exists(args.b):
        print("back_img not exists")
        sys.exit(1)

    # 如果目标目录不存在,则创建它
    if not os.path.exists(target_dir):
        os.makedirs(target_dir)

        # 遍历源目录中的所有文件
    for filename in os.listdir(source_dir):
        # 跳过非图像文件
        if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.gif')):
            continue

        # 构建图像的完整路径
        source_path = os.path.join(source_dir, filename)
        target_path = os.path.join(target_dir, filename)

        # 读取图像
        image_o = cv2.imread(source_path)
        image = cv2.cvtColor(image_o, cv2.COLOR_BGR2GRAY)

        o_shape = image_o.shape[:2]
        # 获取底图
        if args.b is None:
            back_img = generate_random_smooth_black_image(o_shape[0], o_shape[1],args.base_color_random,args.noise_scale,args.k_size)
        else:
            back_img = cv2.imread(args.b)

        # 如果图像是空的,则跳过
        if image is None:
            print(f"Error: Could not open or find the image {source_path}.")
            continue

            # 使用二值化阈值
        _, thresh = cv2.threshold(image, 80, 255, cv2.THRESH_BINARY)

        # 查找轮廓
        contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        # 计算每个轮廓的面积,并按照面积降序排序
        contour_areas = [(cv2.contourArea(contour), contour) for contour in contours]
        contour_areas.sort(key=lambda x: x[0], reverse=True)
        # 提取前三个面积最大的轮廓
        top_n_contours = [contour_area[1] for contour_area in contour_areas[:top_n]]

        # 叠加前三个轮廓到目标图像上
        # 假设轮廓按水平方向排列,并且之间有间隔
        spacing = args.sp  # 轮廓之间的间隔
        start_x = args.x
        start_y = args.y
        target_image = np.copy(back_img)
        for contour in top_n_contours:
            # 创建一个与源图像大小相同的掩码,并用0填充
            mask = np.zeros(image_o.shape[:2], dtype="uint8")

            # 在掩码上绘制当前轮廓(填充为白色)
            cv2.drawContours(mask, [contour], -1, 255, -1)

            # 使用掩码从源图像中提取轮廓区域
            contour_image = cv2.bitwise_and(image_o, image_o, mask=mask)

            x, y, w, h = cv2.boundingRect(contour)
            contour_image = contour_image[y:y + h, x:x + w]
            mask = mask[y:y + h, x:x + w]

            random_y = np.random.randint(0, args.random_y)
            y_coords, x_coords = np.where(mask != 0)
            for y, x in zip(y_coords, x_coords):
                target_image[start_y + y + random_y, start_x + x] = contour_image[y, x]

                # 将轮廓区域叠加到目标图像上
            contour_height, contour_width = contour_image.shape[:2]
            # target_image[0:contour_height, start_x:start_x + contour_width] = contour_image

            # 更新下一个轮廓的起始位置
            start_x += contour_width + spacing
        cv2.imwrite(target_path, target_image)
        print("handel file success:" + source_path)

    print("Process completed. Output images are in the output_images directory.")


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值