使用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.")