1.裁剪图片白框
import cv2
import os
from tqdm import tqdm
def create_file(output_dir_vi, output_dir_ir):
"""
检查并创建输出文件夹
"""
if not os.path.exists(output_dir_vi):
os.makedirs(output_dir_vi)
print(f'创建文件夹: {output_dir_vi}')
if not os.path.exists(output_dir_ir):
os.makedirs(output_dir_ir)
print(f'创建文件夹: {output_dir_ir}')
def update(input_img_path, output_img_path):
"""
裁剪并保存图片
"""
image = cv2.imread(input_img_path)
if image is None:
print(f"警告: 无法读取图片 {input_img_path},跳过处理。")
return
# 确保裁剪范围不超出图片大小
h, w, _ = image.shape
if h < 612 or w < 740:
print(f"警告: 图片 {input_img_path} 尺寸不足以裁剪,跳过处理。")
return
cropped = image[100:612, 100:740] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite(output_img_path, cropped)
# 输入和输出目录
dataset_dir_vi = r'testimg' # 处理前可见光图片目录
output_dir_vi = r'testimg1' # 处理后可见光图片目录
dataset_dir_ir = r'testimgr' # 处理前红外光图片目录
output_dir_ir = r'C:testimgr1' # 处理后红外光图片目录
# 检查文件夹是否存在,如果不存在则创建
create_file(output_dir_vi, output_dir_ir)
# 获取需要处理的图片路径,并生成目标路径
image_filenames_vi = [(os.path.join(dataset_dir_vi, x), os.path.join(output_dir_vi, x))
for x in os.listdir(dataset_dir_vi) if x.lower().endswith(('.png', '.jpg', '.jpeg'))]
image_filenames_ir = [(os.path.join(dataset_dir_ir, x), os.path.join(output_dir_ir, x))
for x in os.listdir(dataset_dir_ir) if x.lower().endswith(('.png', '.jpg', '.jpeg'))]
# 处理可见光图片
print('开始处理可见光图片...')
for path in tqdm(image_filenames_vi):
update(path[0], path[1])
# 处理红外光图片
print('开始处理红外光图片...')
for path in tqdm(image_filenames_ir):
update(path[0], path[1])
2.将xml文件转换为yolo格式
注意两点:1.我将feright car修改为feright_car
2.使用裁剪后的图像路径
mport argparse
from tqdm import tqdm
# 图像类别
classes = ["feright_car", "car", "truck", "bus", "van"]
# 定义相关地址参数
def parse_args():
parser = parser = argparse.ArgumentParser(description='polygon')
parser.add_argument('--in_val_xml_vi_dir', default=r'testlabelr',
help='XML 文件地址')
parser.add_argument('--out_val_txt_vi_dir', default=r'labels',
help=' TXT 输出文件地址')
parser.add_argument('--in_val_img_vi_dir', default=r'testimgr1',
help='裁剪后图片地址')
args = parser.parse_args()
return args
# 根据 xml 文件中的 name 选择生成的 txt 文件中的 id
def select_id(name):
if name == "car":
id = 0
elif name == "truck":
id = 1
elif name == "bus":
id = 2
elif name == "van":
id = 3
elif name == "feright_car":
id = 4
return id
# YOLO 数据处理 - 修改为考虑裁剪偏移
def data_transform(cropped_height, cropped_width, xmin, ymin, xmax, ymax):
# 确保裁剪后的标注框坐标在裁剪范围内
xmin = max(0, xmin - 100) # 减去裁剪偏移量,并确保不小于0
ymin = max(0, ymin - 100)
xmax = min(cropped_width - 1, xmax - 100) # 确保不超过裁剪后的宽高
ymax = min(cropped_height - 1, ymax - 100)
# 中心点坐标 x_c, y_c
x_c = (xmin + xmax) / 2
y_c = (ymin + ymax) / 2
# 计算归一化坐标(裁剪后的宽和高)
x_ = x_c / cropped_width
y_ = y_c / cropped_height
w_ = (xmax - xmin) / cropped_width
h_ = (ymax - ymin) / cropped_height
return x_, y_, w_, h_
# 修改后的 xml2txt 函数
def xml2txt(in_xml_dir, xml_name, out_txt_dir, in_img_dir):
txt_name = xml_name[:-4] + '.txt' # 获取生成的 txt 文件名
txt_path = out_txt_dir # 获取生成的 txt 文件保存地址
# 判断保存 txt 文件的文件夹是否存在,如果不存在则创建相应文件夹
if not os.path.exists(txt_path):
os.makedirs(txt_path)
txt_file = os.path.join(txt_path, txt_name) # 获取 txt 文件地址(保存地址 + 保存名字)
img_name = xml_name[:-4] + '.jpg' # 获取图像名字,确保生成的 txt 文件名与图像文件名一致
img_path = os.path.join(in_img_dir, img_name) # 获取图像地址
img = cv.imread(img_path) # 读取裁剪后的图像
if img is None:
print(f"警告: 无法读取图像 {img_path},跳过处理。")
return
cropped_height, cropped_width, _ = img.shape # 获取裁剪后的图像高度和宽度
xml_file = os.path.join(in_xml_dir, xml_name) # 获取 xml 文件地址
tree = ET.parse(os.path.join(xml_file)) # 使用 ET.parse 方法解析 xml 文件
root = tree.getroot() # 使用 getroot 方法获取根目录
# 生成对应的 txt 文件
with open(txt_file, "w+", encoding='UTF-8') as out_file:
for obj in root.findall('object'):
# 修改部分标注文件中标注不全的 name 文件
name = obj.find('name').text
if name == 'feright car':
name = 'feright_car'
else:
name = name
# 从 xml 文件中提取相关数据信息
if obj.find('polygon'):
xmin, xmax, ymin, ymax = [], [], [], []
polygon = obj.find('polygon')
x1 = int(polygon.find('x1').text)
y1 = int(polygon.find('y1').text)
x2 = int(polygon.find('x2').text)
y2 = int(polygon.find('y2').text)
x3 = int(polygon.find('x3').text)
y3 = int(polygon.find('y3').text)
x4 = int(polygon.find('x4').text)
y4 = int(polygon.find('y4').text)
for i in [x1, x2, x3, x4]:
xmin.append(i)
xmax.append(i)
for j in [y1, y2, y3, y4]:
ymin.append(j)
ymax.append(j)
xmin = min(xmin)
xmax = max(xmax)
ymin = min(ymin)
ymax = max(ymax)
# 如果标注框完全在裁剪范围外,跳过
if xmax < 100 or ymax < 100 or xmin > 740 or ymin > 612:
continue
# yolo 格式转换(考虑裁剪范围偏移)
result = data_transform(cropped_height, cropped_width, xmin, ymin, xmax, ymax)
result_id = select_id(name)
elif obj.find('bndbox'):
bndbox = obj.find('bndbox')
xmin = int(bndbox.find('xmin').text)
ymin = int(bndbox.find('ymin').text)
xmax = int(bndbox.find('xmax').text)
ymax = int(bndbox.find('ymax').text)
# 如果标注框完全在裁剪范围外,跳过
if xmax < 100 or ymax < 100 or xmin > 740 or ymin > 612:
continue
# yolo 格式转换(考虑裁剪范围偏移)
result = data_transform(cropped_height, cropped_width, xmin, ymin, xmax, ymax)
result_id = select_id(name)
# 创建 txt 文件中的数据
data = str(result[0]) + " " + str(result[1]) + " " + str(result[2]) + " " + str(result[3]) + '\n'
data = str(result_id) + " " + data
out_file.write(data)
if __name__ == "__main__":
args = parse_args() # 获取命令参数
xml_ir_path = args.in_val_xml_vi_dir # 获取可见光 val xml 文件地址
xmlFiles_ir = os.listdir(xml_ir_path) # 生成可见光 val xml 文件名列表
print('Start transforming infrared labels...')
for i in tqdm(range(0, len(xmlFiles_ir))):
xml2txt(args.in_val_xml_vi_dir, xmlFiles_ir[i], args.out_val_txt_vi_dir, args.in_val_img_vi_dir)
print('Finish.')
3.展示
import cv2
img = cv2.imread(r'testimgr1\00001.jpg')
with open(r'labels\00001.txt', 'r') as f:
for line in f.readlines():
# temp = f.read()
temp = line.split()
x_, y_, w_, h_ = eval(temp[1]), eval(temp[2]), eval(temp[3]), eval(temp[4])
x1, y1, x2, y2 = int((x_ - w_ / 2) * img.shape[1]), int((y_ - h_ / 2) * img.shape[0]), \
int((x_ + w_ / 2) * img.shape[1]), int((y_ + h_ / 2) * img.shape[0])
cv2.rectangle(img, (x1, y1), (x2, y2), (255, 0, 0))
cv2.imshow('windows', img)
cv2.waitKey(0)