2025-03-12 01:38:47 +00:00
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
2025-03-09 14:36:22 +00:00
|
|
|
|
import random
|
|
|
|
|
import cv2
|
|
|
|
|
import numpy as np
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
# 假设我们有一个函数 `remove_background` 使用某种方法去背景,返回前景掩码和前景图像
|
|
|
|
|
def remove_background(image_path):
|
|
|
|
|
# 加载图像
|
|
|
|
|
source_image = cv2.imread(image_path)
|
|
|
|
|
# 这里可以使用一个预训练的模型去背景,比如 U2-Net。为了简化,假设我们得到一个二值掩码
|
|
|
|
|
# 掩码生成逻辑可以替换为实际的模型推理
|
|
|
|
|
# 转换为灰度图像
|
|
|
|
|
GRAY = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
|
|
|
|
|
# 二值化处理
|
|
|
|
|
_, mask_threshold = cv2.threshold(GRAY, 0, 1, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
|
|
|
|
|
|
|
|
|
|
# 定义结构元素
|
|
|
|
|
element = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
|
|
|
|
|
element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
|
|
|
|
|
|
|
|
|
|
# 膨胀和腐蚀操作
|
|
|
|
|
mask_dilate = cv2.dilate(mask_threshold, element)
|
|
|
|
|
mask_erode = cv2.erode(mask_dilate, element1)
|
|
|
|
|
|
|
|
|
|
# 计算非零像素数量
|
|
|
|
|
count2 = cv2.countNonZero(mask_erode)
|
|
|
|
|
|
|
|
|
|
# 查找轮廓
|
|
|
|
|
contours, hierarchy = cv2.findContours(mask_erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)
|
|
|
|
|
|
|
|
|
|
# 过滤轮廓
|
|
|
|
|
contours = [c for c in contours if cv2.contourArea(c) >= count2 * 0.3]
|
|
|
|
|
|
|
|
|
|
# 绘制轮廓
|
|
|
|
|
mask = np.zeros_like(mask_erode)
|
|
|
|
|
cv2.drawContours(mask, contours, -1, 1, -1)
|
|
|
|
|
|
|
|
|
|
# 将掩码转换为3通道
|
|
|
|
|
mask_cvtColor = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
# 应用掩码
|
|
|
|
|
source_image_multiply = cv2.multiply(source_image, mask_cvtColor)
|
|
|
|
|
|
|
|
|
|
# 转换为HSV颜色空间
|
|
|
|
|
imgHSV = cv2.cvtColor(source_image_multiply, cv2.COLOR_BGR2HSV)
|
|
|
|
|
|
|
|
|
|
# 定义HSV范围
|
|
|
|
|
scalarL = np.array([0, 46, 46])
|
|
|
|
|
scalarH = np.array([45, 255, 255])
|
|
|
|
|
|
|
|
|
|
# 根据HSV范围生成掩码
|
|
|
|
|
mask_inRange = cv2.inRange(imgHSV, scalarL, scalarH)
|
|
|
|
|
|
|
|
|
|
# 二值化处理
|
|
|
|
|
_, mask_tthreshold = cv2.threshold(mask_inRange, 0, 1, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
|
|
|
|
|
|
|
|
|
|
# 中值滤波
|
|
|
|
|
mask_medianBlur = cv2.medianBlur(mask_tthreshold, 7)
|
|
|
|
|
|
|
|
|
|
# 将掩码转换为3通道
|
|
|
|
|
mask_scvtColor = cv2.cvtColor(mask_medianBlur, cv2.COLOR_GRAY2BGR)
|
|
|
|
|
|
|
|
|
|
# 应用掩码
|
|
|
|
|
source_image = cv2.multiply(source_image, mask_scvtColor)
|
|
|
|
|
|
|
|
|
|
return source_image
|
|
|
|
|
|
|
|
|
|
def synthesize_background(foreground, background):
|
|
|
|
|
|
|
|
|
|
# 创建前景掩膜(非黑色区域)
|
|
|
|
|
gray = cv2.cvtColor(foreground, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
_, mask = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY) # 阈值设为1以保留所有非纯黑像素
|
|
|
|
|
|
|
|
|
|
mask = cv2.GaussianBlur(mask, (5,5), 0) # 高斯模糊柔化边缘
|
|
|
|
|
_, mask = cv2.threshold(mask, 200, 255, cv2.THRESH_BINARY) # 重新二值化
|
|
|
|
|
|
|
|
|
|
# 精准形态学处理
|
|
|
|
|
kernel = np.ones((2,2), np.uint8)
|
|
|
|
|
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=1) # 闭运算填充小孔
|
|
|
|
|
|
|
|
|
|
# 反转掩膜用于获取背景区域
|
|
|
|
|
mask_inv = cv2.bitwise_not(mask)
|
|
|
|
|
|
|
|
|
|
# 提取背景和前景的ROI区域
|
|
|
|
|
background_roi = cv2.bitwise_and(background, background, mask=mask_inv)
|
|
|
|
|
foreground_roi = cv2.bitwise_and(foreground, foreground, mask=mask)
|
|
|
|
|
|
|
|
|
|
# 合成图像
|
|
|
|
|
result = cv2.add(foreground_roi, background_roi)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def edge_fill2(img):
|
|
|
|
|
(height, width, p) = img.shape
|
|
|
|
|
H = 2384
|
|
|
|
|
W = 1560
|
|
|
|
|
top = bottom=int((W - height) / 2)
|
|
|
|
|
left= right= int((H - width) / 2)
|
|
|
|
|
img_result = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0)
|
|
|
|
|
return img_result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_images(input_folder, background_image_path, output_base):
|
|
|
|
|
"""
|
|
|
|
|
递归处理所有子文件夹并保持目录结构
|
|
|
|
|
"""
|
|
|
|
|
# 预处理背景路径(只需执行一次)
|
2025-03-12 01:38:47 +00:00
|
|
|
|
# if os.path.isfile(background_image_path):
|
|
|
|
|
# background_paths = [background_image_path]
|
|
|
|
|
# else:
|
|
|
|
|
# valid_ext = ['.jpg', '.jpeg', '.png', '.bmp', '.webp']
|
|
|
|
|
# background_paths = [
|
|
|
|
|
# os.path.join(background_image_path, f)
|
|
|
|
|
# for f in os.listdir(background_image_path)
|
|
|
|
|
# if os.path.splitext(f)[1].lower() in valid_ext
|
|
|
|
|
# ]
|
2025-03-09 14:36:22 +00:00
|
|
|
|
|
|
|
|
|
# 递归遍历输入目录
|
|
|
|
|
for root, dirs, files in os.walk(input_folder):
|
|
|
|
|
# 计算相对路径
|
|
|
|
|
relative_path = os.path.relpath(root, input_folder)
|
|
|
|
|
|
|
|
|
|
# 创建对应的输出目录
|
|
|
|
|
output_dir = os.path.join(output_base, relative_path)
|
|
|
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# 处理当前目录的文件
|
|
|
|
|
for filename in files:
|
|
|
|
|
input_path = os.path.join(root, filename)
|
|
|
|
|
output_path = os.path.join(output_dir, filename)
|
|
|
|
|
|
|
|
|
|
# 跳过非图像文件
|
|
|
|
|
if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 去背景处理
|
2025-03-12 01:38:47 +00:00
|
|
|
|
result = remove_background(input_path)
|
2025-03-09 14:36:22 +00:00
|
|
|
|
|
|
|
|
|
|
2025-03-12 01:38:47 +00:00
|
|
|
|
# result = edge_fill2(result)
|
2025-03-09 14:36:22 +00:00
|
|
|
|
|
|
|
|
|
# 保存结果
|
|
|
|
|
cv2.imwrite(output_path, result)
|
|
|
|
|
print(f"Processed: {input_path} -> {output_path}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error processing {input_path}: {str(e)}")
|
2025-03-12 01:38:47 +00:00
|
|
|
|
|
|
|
|
|
def process_single_file(input_path, output_path):
|
|
|
|
|
"""处理单个文件的独立函数"""
|
|
|
|
|
try:
|
|
|
|
|
result = remove_background(input_path)
|
|
|
|
|
# result = edge_fill2(result) # 按需启用
|
|
|
|
|
cv2.imwrite(output_path, result)
|
|
|
|
|
print(f"Processed: {input_path} -> {output_path}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error processing {input_path}: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def process_imageswithpool(input_folder, background_image_path, output_base):
|
|
|
|
|
"""
|
|
|
|
|
多线程版本的处理函数
|
|
|
|
|
使用ThreadPoolExecutor并行处理文件
|
|
|
|
|
"""
|
|
|
|
|
with ThreadPoolExecutor(max_workers=os.cpu_count()*2) as executor:
|
|
|
|
|
futures = []
|
|
|
|
|
for root, dirs, files in os.walk(input_folder):
|
|
|
|
|
# 创建输出目录(主线程保证目录创建顺序)
|
|
|
|
|
relative_path = os.path.relpath(root, input_folder)
|
|
|
|
|
output_dir = os.path.join(output_base, relative_path)
|
|
|
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# 提交任务到线程池
|
|
|
|
|
for filename in files:
|
|
|
|
|
if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
input_path = os.path.join(root, filename)
|
|
|
|
|
output_path = os.path.join(output_dir, filename)
|
|
|
|
|
futures.append(executor.submit(
|
|
|
|
|
process_single_file,
|
|
|
|
|
input_path,
|
|
|
|
|
output_path
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
# 可选:等待所有任务完成并处理异常
|
|
|
|
|
for future in futures:
|
|
|
|
|
try:
|
|
|
|
|
future.result()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Unhandled error in thread: {str(e)}")
|
2025-03-09 14:36:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 使用示例
|
2025-03-12 01:38:47 +00:00
|
|
|
|
input_directory = 'L:/Grade_datasets/JY_A'
|
2025-03-09 14:36:22 +00:00
|
|
|
|
background_image_path = 'F:/dataset/02.TA_EC/rundata/BACKGROUND/ZY_B'
|
2025-03-12 01:38:47 +00:00
|
|
|
|
output_directory = 'L:/Grade_datasets/MOVE_BACKGROUND'
|
2025-03-09 14:36:22 +00:00
|
|
|
|
|
2025-03-12 01:38:47 +00:00
|
|
|
|
process_imageswithpool(input_directory, background_image_path, output_directory)
|