123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- 测试数据生成器
- 用于生成模拟的测试数据,方便测试模型评估功能
- 作者: AI Assistant
- 创建时间: 2024
- """
- import os
- import cv2
- import numpy as np
- import json
- import random
- from datetime import datetime
- from typing import List, Dict, Tuple
- class TestDataGenerator:
- """
- 测试数据生成器
- 生成模拟的UAV和鸟类图像用于测试
- """
-
- def __init__(self, output_dir: str = None):
- """
- 初始化生成器
-
- Args:
- output_dir: 输出目录
- """
- if output_dir is None:
- output_dir = os.path.join(os.path.dirname(__file__), 'Data')
-
- self.output_dir = output_dir
- self.image_size = (640, 480) # 默认图像尺寸
-
- # 创建目录结构
- self.birds_dir = os.path.join(output_dir, 'BIRDS')
- self.uav_dir = os.path.join(output_dir, 'UAV')
- self.others_dir = os.path.join(output_dir, 'OTHERS')
-
- os.makedirs(self.birds_dir, exist_ok=True)
- os.makedirs(self.uav_dir, exist_ok=True)
- os.makedirs(self.others_dir, exist_ok=True)
-
- def generate_bird_image(self, image_id: int) -> str:
- """
- 生成鸟类图像(模拟)
-
- Args:
- image_id: 图像ID
-
- Returns:
- 生成的图像路径
- """
- # 创建蓝天背景
- img = np.ones((self.image_size[1], self.image_size[0], 3), dtype=np.uint8)
- img[:, :] = [135, 206, 235] # 天蓝色背景
-
- # 添加一些云朵
- for _ in range(random.randint(2, 5)):
- center = (random.randint(50, self.image_size[0]-50),
- random.randint(50, self.image_size[1]-50))
- radius = random.randint(20, 60)
- cv2.circle(img, center, radius, (255, 255, 255), -1)
-
- # 绘制鸟类形状(简化的V形)
- bird_x = random.randint(100, self.image_size[0]-100)
- bird_y = random.randint(100, self.image_size[1]-100)
- bird_size = random.randint(15, 40)
-
- # 绘制鸟的翅膀(V形)
- pts1 = np.array([[bird_x-bird_size, bird_y],
- [bird_x, bird_y-bird_size//2],
- [bird_x, bird_y+bird_size//2]], np.int32)
- pts2 = np.array([[bird_x+bird_size, bird_y],
- [bird_x, bird_y-bird_size//2],
- [bird_x, bird_y+bird_size//2]], np.int32)
-
- cv2.fillPoly(img, [pts1], (50, 50, 50)) # 深灰色鸟
- cv2.fillPoly(img, [pts2], (50, 50, 50))
-
- # 添加噪声
- noise = np.random.normal(0, 10, img.shape).astype(np.uint8)
- img = cv2.add(img, noise)
-
- # 保存图像
- filename = f"bird_{image_id:04d}.jpg"
- filepath = os.path.join(self.birds_dir, filename)
- cv2.imwrite(filepath, img)
-
- return filepath
-
- def generate_uav_image(self, image_id: int) -> str:
- """
- 生成无人机图像(模拟)
-
- Args:
- image_id: 图像ID
-
- Returns:
- 生成的图像路径
- """
- # 创建蓝天背景
- img = np.ones((self.image_size[1], self.image_size[0], 3), dtype=np.uint8)
- img[:, :] = [135, 206, 235] # 天蓝色背景
-
- # 添加一些云朵
- for _ in range(random.randint(1, 3)):
- center = (random.randint(50, self.image_size[0]-50),
- random.randint(50, self.image_size[1]-50))
- radius = random.randint(30, 80)
- cv2.circle(img, center, radius, (255, 255, 255), -1)
-
- # 绘制无人机形状(简化的十字形)
- uav_x = random.randint(100, self.image_size[0]-100)
- uav_y = random.randint(100, self.image_size[1]-100)
- uav_size = random.randint(20, 50)
-
- # 绘制无人机机身(矩形)
- cv2.rectangle(img,
- (uav_x-uav_size//4, uav_y-uav_size//8),
- (uav_x+uav_size//4, uav_y+uav_size//8),
- (80, 80, 80), -1)
-
- # 绘制螺旋桨臂
- cv2.line(img, (uav_x-uav_size, uav_y), (uav_x+uav_size, uav_y), (60, 60, 60), 3)
- cv2.line(img, (uav_x, uav_y-uav_size), (uav_x, uav_y+uav_size), (60, 60, 60), 3)
-
- # 绘制螺旋桨(圆形)
- for dx, dy in [(-uav_size, 0), (uav_size, 0), (0, -uav_size), (0, uav_size)]:
- cv2.circle(img, (uav_x+dx, uav_y+dy), uav_size//6, (40, 40, 40), -1)
-
- # 添加噪声
- noise = np.random.normal(0, 8, img.shape).astype(np.uint8)
- img = cv2.add(img, noise)
-
- # 保存图像
- filename = f"uav_{image_id:04d}.jpg"
- filepath = os.path.join(self.uav_dir, filename)
- cv2.imwrite(filepath, img)
-
- return filepath
-
- def generate_other_image(self, image_id: int) -> str:
- """
- 生成其他类型图像(负样本)
-
- Args:
- image_id: 图像ID
-
- Returns:
- 生成的图像路径
- """
- # 随机选择背景类型
- bg_type = random.choice(['sky', 'landscape', 'urban'])
-
- if bg_type == 'sky':
- # 纯天空背景
- img = np.ones((self.image_size[1], self.image_size[0], 3), dtype=np.uint8)
- img[:, :] = [135, 206, 235] # 天蓝色
-
- # 添加云朵
- for _ in range(random.randint(3, 8)):
- center = (random.randint(0, self.image_size[0]),
- random.randint(0, self.image_size[1]))
- radius = random.randint(40, 100)
- cv2.circle(img, center, radius, (255, 255, 255), -1)
-
- elif bg_type == 'landscape':
- # 风景背景
- img = np.ones((self.image_size[1], self.image_size[0], 3), dtype=np.uint8)
-
- # 天空部分
- img[:self.image_size[1]//2, :] = [135, 206, 235]
-
- # 地面部分
- img[self.image_size[1]//2:, :] = [34, 139, 34] # 森林绿
-
- # 添加一些树木形状
- for _ in range(random.randint(5, 10)):
- tree_x = random.randint(0, self.image_size[0])
- tree_y = random.randint(self.image_size[1]//2, self.image_size[1])
- tree_height = random.randint(30, 80)
- cv2.rectangle(img,
- (tree_x-5, tree_y),
- (tree_x+5, tree_y-tree_height),
- (139, 69, 19), -1) # 棕色树干
- cv2.circle(img, (tree_x, tree_y-tree_height), 15, (0, 100, 0), -1) # 绿色树冠
-
- else: # urban
- # 城市背景
- img = np.ones((self.image_size[1], self.image_size[0], 3), dtype=np.uint8)
- img[:, :] = [169, 169, 169] # 灰色背景
-
- # 添加建筑物
- for _ in range(random.randint(3, 6)):
- building_x = random.randint(0, self.image_size[0]-50)
- building_y = random.randint(self.image_size[1]//3, self.image_size[1])
- building_w = random.randint(30, 80)
- building_h = random.randint(50, 150)
-
- cv2.rectangle(img,
- (building_x, building_y),
- (building_x+building_w, building_y-building_h),
- (105, 105, 105), -1) # 深灰色建筑
-
- # 添加噪声
- noise = np.random.normal(0, 12, img.shape).astype(np.uint8)
- img = cv2.add(img, noise)
-
- # 保存图像
- filename = f"other_{image_id:04d}.jpg"
- filepath = os.path.join(self.others_dir, filename)
- cv2.imwrite(filepath, img)
-
- return filepath
-
- def generate_dataset(self, num_birds: int = 50, num_uavs: int = 50, num_others: int = 100) -> Dict:
- """
- 生成完整的测试数据集
-
- Args:
- num_birds: 鸟类图像数量
- num_uavs: 无人机图像数量
- num_others: 其他图像数量
-
- Returns:
- 数据集信息字典
- """
- print(f"开始生成测试数据集...")
- print(f"鸟类图像: {num_birds} 张")
- print(f"无人机图像: {num_uavs} 张")
- print(f"其他图像: {num_others} 张")
- print(f"输出目录: {self.output_dir}")
-
- dataset_info = {
- 'creation_time': datetime.now().isoformat(),
- 'total_images': num_birds + num_uavs + num_others,
- 'categories': {
- 'birds': {'count': num_birds, 'files': []},
- 'uavs': {'count': num_uavs, 'files': []},
- 'others': {'count': num_others, 'files': []}
- }
- }
-
- # 生成鸟类图像
- print("\n生成鸟类图像...")
- for i in range(num_birds):
- filepath = self.generate_bird_image(i)
- dataset_info['categories']['birds']['files'].append(filepath)
- if (i + 1) % 10 == 0:
- print(f"已生成 {i + 1}/{num_birds} 张鸟类图像")
-
- # 生成无人机图像
- print("\n生成无人机图像...")
- for i in range(num_uavs):
- filepath = self.generate_uav_image(i)
- dataset_info['categories']['uavs']['files'].append(filepath)
- if (i + 1) % 10 == 0:
- print(f"已生成 {i + 1}/{num_uavs} 张无人机图像")
-
- # 生成其他图像
- print("\n生成其他图像...")
- for i in range(num_others):
- filepath = self.generate_other_image(i)
- dataset_info['categories']['others']['files'].append(filepath)
- if (i + 1) % 20 == 0:
- print(f"已生成 {i + 1}/{num_others} 张其他图像")
-
- # 保存数据集信息
- info_file = os.path.join(self.output_dir, 'dataset_info.json')
- with open(info_file, 'w', encoding='utf-8') as f:
- json.dump(dataset_info, f, ensure_ascii=False, indent=2)
-
- print(f"\n数据集生成完成!")
- print(f"总计生成 {dataset_info['total_images']} 张图像")
- print(f"数据集信息已保存到: {info_file}")
-
- return dataset_info
-
- def create_annotation_file(self, dataset_info: Dict) -> str:
- """
- 创建标注文件
-
- Args:
- dataset_info: 数据集信息
-
- Returns:
- 标注文件路径
- """
- annotations = {
- 'info': {
- 'description': 'Generated test dataset for UAV and bird detection',
- 'version': '1.0',
- 'creation_date': dataset_info['creation_time']
- },
- 'images': []
- }
-
- # 添加鸟类图像标注
- for filepath in dataset_info['categories']['birds']['files']:
- annotations['images'].append({
- 'path': filepath,
- 'label': 'bird',
- 'has_target': True,
- 'bboxes': [] # 简化版本,不包含具体边界框
- })
-
- # 添加无人机图像标注
- for filepath in dataset_info['categories']['uavs']['files']:
- annotations['images'].append({
- 'path': filepath,
- 'label': 'uav',
- 'has_target': True,
- 'bboxes': []
- })
-
- # 添加其他图像标注
- for filepath in dataset_info['categories']['others']['files']:
- annotations['images'].append({
- 'path': filepath,
- 'label': 'others',
- 'has_target': False,
- 'bboxes': []
- })
-
- # 保存标注文件
- annotation_file = os.path.join(self.output_dir, 'annotations.json')
- with open(annotation_file, 'w', encoding='utf-8') as f:
- json.dump(annotations, f, ensure_ascii=False, indent=2)
-
- print(f"标注文件已保存到: {annotation_file}")
- return annotation_file
- def main():
- """
- 主函数 - 生成测试数据
- """
- print("测试数据生成器")
- print("=" * 50)
-
- # 配置参数
- output_dir = r"d:\PythonProject\R360-UAVmodelTool\Data"
- num_birds = 30
- num_uavs = 30
- num_others = 60
-
- # 初始化生成器
- generator = TestDataGenerator(output_dir)
-
- # 生成数据集
- dataset_info = generator.generate_dataset(num_birds, num_uavs, num_others)
-
- # 创建标注文件
- annotation_file = generator.create_annotation_file(dataset_info)
-
- print("\n数据集结构:")
- print(f"├── BIRDS/ ({num_birds} 张图像)")
- print(f"├── UAV/ ({num_uavs} 张图像)")
- print(f"├── OTHERS/ ({num_others} 张图像)")
- print(f"├── dataset_info.json")
- print(f"└── annotations.json")
-
- print("\n现在可以运行 model_evaluator.py 进行模型评估!")
- if __name__ == "__main__":
- main()
|