如何用python编写一个绘制马赛克图像的自写程序mask = np.zeros

本教程教您如何使用Python和Pygame创建一个自写程序,该程序能将任意图片转化为马赛克画。通过合并像素生成马赛克色块,最终生成一幅蒙娜丽莎马赛克画。
Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。
这篇教程将会展示如何用python的图形化包“Pygame”和基础的文件I/O来创建一个自写程序。这个程序将可以仿照给定的图片来创作一幅马赛克画。Python是一种非常强大的编程语言。它有着许多的内建函数和可下载的软件包。在这片教程的末尾,你将会拥有一副属于你自己的蒙娜丽莎马赛克画并且将会学会如何对所有你喜欢的图片进行处理。注意,本文是为那些对Python,Pygame和文件I/O有着深刻理解的用户准备的。
第一部分 下载图片
1.下载《蒙娜丽莎》的图片。这是创建你的马赛克画的基础。注意这幅图像的尺寸(以像素为单位)将对所要写的代码有重要影响。
·将它保存为“mona.jpg”
·图像大小设置为743像素 * 1155像素。确保图片是以这个尺寸保存的,否则将不会得到正确地马赛克图片。
第二部分 创建主程序
1.打开一个新的Python文件。将其命名为“makeMona.py”(可自定义)。
2.导入所需要的模块。这个程序需要Pygame模块来显示图片并且需要Math模块来执行你所添加的功能。
3.创建显示图片的部分。在开始处理图像以前,需要创建显示窗口以使Mona Lisa图像能够被加载并且显示出来
·display.set_mode((743,1155))是对于显示尺寸的设置。(743,1155)代表显示的尺寸;注意这要和图像的尺寸一致。
4为自写程序创建一个目标文件。在“makeMona”程序中,设置要写入的新文件。
·变量mon是您对于马赛克文件的引用。声明中新文件叫做“mona.py”。“w”代表将会对新文件进行写入的操作。
5初始化新程序。现在可以开始写入“mona.py”文件。在这里你需要导入新文件所需的模块以及设置显示图片的尺寸。
·mon.write表示现在正把下面的代码写入到新文件中。其中设置的显示尺寸要和Mona Lisa图像的尺寸一致。
6在makeMona.py中显示图片。为了确认你的图片已经正确地加载到你的程序中,你需要让图片显示出来。
7.绘制马赛克图片。现在可以将图片分解为小的色块来创建马赛克图像。为了得到最好的马赛克效果而不至于使得图像变形,建议将五个像素合并到一个色块内。
·mon.write("draw.rect(screen,"+c+",("+str(x)+","+str(y)+",4,4)) ")这是最重要的一行,分析如下:
·draw.rect(screen,"+c+", 用来描绘一个个马赛克色块(屏幕上显示出来的一个个小矩形) 然后将颜色"c"关联到屏幕的对应位置 str(screen.get_at((x,y)))。这是如何将颜色映射到每个像素的过程。
·("+str(x)+","+str(y)中的"x"和"y"代表色块的坐标。用str来转换是由于程序需要按处理字符串的方式来处理这些坐标值(这是因为现在这些值是在三引号中,而所有引号中的值都是string格式)
·+",4,4)) ")中的4,4用来表示马赛克华中每个色块的尺寸,“”是换行符,用来是程序切换到新的一行来写接下来的代码。
8.创建while-running循环。像所有的Pygame程序一样,你必须在mona.py中写入while-running循环。
第三部分 代码的检查和测试
1.检查代码。你的主程序makeMona.py现在已经完成了。以下是完整的代码。
2.打开新生成的mona.py文件。在你makeMona程序所保存的目录下,可以找到新生成的mona.py程序文件。
·如果你打开这个文件,你将会发现几百行代码。这简直就像魔法一般,每行代码都对应着马赛克画中的一个独立色块。
·打开这个文件可能需要几秒钟的时间;因为它非常的大。
3.运行mona.py。如果之前的每一步都没有问题,最后一步只需要运行mona.py来看下你的《蒙娜丽莎》马赛克画。
第四部分 常见问题
1 排除程序故障。在实现你自己的马赛克代码中很容易犯一些小错误。以下是一些会遇到的常见问题以及解决方法。
·新生成的程序文件在哪?主程序运行完毕后,新程序文件应当出现在主程序所在的目录中。
·新生成的程序不能执行。你为自写程序所写的所有代码都在三引号之中。检查所有引号之中的代码,确保没有丢掉冒号引号或者缩进方面的错误。
·为什么图像扭曲了?这可能是由于错误的显示尺寸所造成的。确保display.set_mode中的显示尺寸在整个代码中是相同的。http://www.wikihow.com/Make-a-Self%E2%80%90Writing-Mosaic-Program-in-Python
import os import sys import cv2 import torch import yaml import numpy as np from pathlib import Path from ultralytics import YOLO from datetime import datetime import random import albumentations as A from sklearn.preprocessing import minmax_scale # 增强的光圈模拟函数(添加颜色变化) def simulate_halo(img, center, radius, intensity_range=(180, 255)): """模拟真实的光圈效果,引入颜色偏移""" # 随机选择光圈类型 halo_type = random.choice(["gaussian", "radial", "composite", "chromatic"]) if halo_type == "chromatic": # 色差光圈(不同颜色通道偏移) halo_radius = int(radius * random.uniform(1.5, 3.0)) offset_x = random.randint(-5, 5) offset_y = random.randint(-5, 5) # 分别处理每个颜色通道 halo_img = np.zeros_like(img) for c_idx, color in enumerate([(255, 0, 0), (0, 255, 0), (0, 0, 255)]): # RGB通道 channel_img = np.zeros_like(img) offset_center = (center[0] + offset_x*(c_idx-1), center[1] + offset_y*(c_idx-1)) cv2.circle(channel_img, offset_center, halo_radius, color, -1) # 应用高斯模糊 blur_size = 2 * int(halo_radius * 0.8) + 1 channel_img = cv2.GaussianBlur(channel_img, (blur_size, blur_size), halo_radius * 0.5) # 叠加到主图像 halo_img = cv2.add(halo_img, channel_img) # 应用光圈 alpha = random.uniform(0.3, 0.7) img = cv2.addWeighted(img, 1, halo_img, alpha, 0) return img if halo_type == "gaussian": # 高斯分布光圈 halo_intensity = random.randint(*intensity_range) halo_radius = int(radius * random.uniform(1.5, 3.0)) # 创建光圈遮罩 mask = np.zeros_like(img[..., 0], dtype=np.float32) cv2.circle(mask, center, halo_radius, 1.0, -1) # 应用高斯模糊 blur_size = 2 * int(halo_radius * 0.8) + 1 mask = cv2.GaussianBlur(mask, (blur_size, blur_size), halo_radius * 0.5) # 归一化并应用光圈 mask = mask / np.max(mask) for c in range(3): img[..., c] = np.clip(img[..., c] + mask * halo_intensity, 0, 255).astype(np.uint8) elif halo_type == "radial": # 径向渐变光圈 halo_intensity = random.randint(*intensity_range) inner_radius = int(radius * random.uniform(0.8, 1.2)) outer_radius = int(radius * random.uniform(2.0, 4.0)) # 创建渐变光圈 for r in range(inner_radius, outer_radius + 1): alpha = 1.0 - (r - inner_radius) / (outer_radius - inner_radius) intensity = int(halo_intensity * alpha) cv2.circle(img, center, r, (intensity, intensity, intensity), 1) elif halo_type == "composite": # 组合光圈(高斯+径向) # 先添加径向渐变 simulate_halo(img, center, radius, intensity_range) # 再添加高斯核心 simulate_halo(img, center, int(radius * 0.5), (intensity_range[0] + 50, intensity_range[1])) return img # 其他光圈类型保持不变... # [原代码中的gaussian, radial, composite实现] # 改进的负样本生成 def add_negative_samples(img, label_positions, num_negatives=5): """添加多样化的负样本(非目标物体)""" height, width = img.shape[:2] negative_shapes = [] # 避免负样本与真实目标重叠 safe_positions = [] attempts = 0 while len(safe_positions) < num_negatives and attempts < num_negatives * 3: x = random.randint(20, width-20) y = random.randint(20, height-20) # 检查与真实目标的距离 too_close = False for (cx, cy, r) in label_positions: distance = np.sqrt((x - cx)**2 + (y - cy)**2) if distance < r * 3: # 保持安全距离 too_close = True break if not too_close: safe_positions.append((x, y)) attempts += 1 # 生成不同类型和颜色的负样本 for x, y in safe_positions: shape_type = random.choice(["circle", "square", "triangle", "star", "line"]) color = ( random.randint(0, 255), # R random.randint(0, 255), # G random.randint(0, 255) # B ) size = random.randint(3, 15) if shape_type == "circle": cv2.circle(img, (x, y), size, color, -1) negative_shapes.append(("circle", x, y, size)) elif shape_type == "square": half = size // 2 cv2.rectangle(img, (x-half, y-half), (x+half, y+half), color, -1) negative_shapes.append(("square", x, y, size)) elif shape_type == "triangle": pts = np.array([ [x, y-size], [x-size, y+size], [x+size, y+size] ], dtype=np.int32) cv2.fillPoly(img, [pts], color) negative_shapes.append(("triangle", x, y, size)) elif shape_type == "star": # 简易星形 pts = [] for i in range(5): angle = np.pi/2 + i*2*np.pi/5 outer_x = int(x + size * np.cos(angle)) outer_y = int(y + size * np.sin(angle)) inner_x = int(x + size/2 * np.cos(angle + np.pi/5)) inner_y = int(y + size/2 * np.sin(angle + np.pi/5)) pts.extend([(outer_x, outer_y), (inner_x, inner_y)]) cv2.fillPoly(img, [np.array(pts, dtype=np.int32)], color) negative_shapes.append(("star", x, y, size)) elif shape_type == "line": length = random.randint(10, 30) angle = random.uniform(0, 2*np.pi) end_x = int(x + length * np.cos(angle)) end_y = int(y + length * np.sin(angle)) thickness = random.randint(1, 3) cv2.line(img, (x, y), (end_x, end_y), color, thickness) negative_shapes.append(("line", x, y, length)) return img, negative_shapes # 改进的数据集生成(添加负样本和颜色变化) def create_robust_dataset(output_dir="black_dot_dataset", halo_prob=0.7): """创建带有多样性背景、光圈效果和负样本的训练数据集""" # ... [背景生成代码保持不变] ... output_path = Path(output_dir) # 创建目录结构 (output_path/"images/train").mkdir(parents=True, exist_ok=True) (output_path/"labels/train").mkdir(parents=True, exist_ok=True) print(f"🔧 创建数据集于: {output_path.resolve()}") print(f" 光圈出现概率: {halo_prob*100}%") # 可能的背景变化(灰度值范围) bg_ranges = [ (60, 100), # 标准背景 (100, 140), # 明亮背景 (30, 60), # 暗背景 (50, 90), # 中等背景 (120, 170) # 高亮背景(模拟过曝) ] # 光圈强度范围 halo_intensities = [ (180, 220), # 中等光圈 (220, 255), # 强光圈 (150, 190), # 弱光圈 (200, 255) # 极强光圈 ] # 背景模式 modes = ["uniform", "gradient", "noisy", "textured"] for i in range(1000): # 1000张训练图像 # 随机选择背景类型 bg_mode = random.choice(modes) bg_min, bg_max = random.choice(bg_ranges) # 创建基础背景 if bg_mode == "uniform": # 均匀背景 base_gray = np.random.randint(bg_min, bg_max) img = np.full((640, 640, 3), base_gray, dtype=np.uint8) elif bg_mode == "gradient": # 渐变背景 img = np.zeros((640, 640, 3), dtype=np.uint8) start_gray = np.random.randint(bg_min, bg_max) end_gray = np.random.randint(bg_min, bg_max) for y in range(640): gray_val = int(start_gray + (end_gray - start_gray) * y / 640) img[y, :, :] = gray_val elif bg_mode == "noisy": # 噪声背景 base_gray = np.random.randint(bg_min, bg_max) img = np.full((640, 640, 3), base_gray, dtype=np.uint8) noise = np.random.normal(0, 15, (640, 640, 3)).astype(np.uint8) img = cv2.add(img, noise) elif bg_mode == "textured": # 纹理背景(模拟表面不平整) base_gray = np.random.randint(bg_min, bg_max) img = np.full((640, 640, 3), base_gray, dtype=np.uint8) # 添加纹理噪声 for _ in range(50): x = np.random.randint(0, 640) y = np.random.randint(0, 640) size = np.random.randint(10, 100) intensity = np.random.randint(-20, 20) cv2.circle(img, (x, y), size, (base_gray + intensity, base_gray + intensity, base_gray + intensity), -1) # 添加颜色偏移增强数据多样性 color_shift = random.uniform(0.9, 1.1) img = (img * color_shift).clip(0, 255).astype(np.uint8) num_dots = np.random.randint(3, 15) label_lines = [] dot_positions = [] for dot_idx in range(num_dots): # ... [圆点生成代码保持不变] ... # 随机位置和大小(更小的圆点) x = np.random.randint(20, 620) y = np.random.randint(20, 620) radius = np.random.randint(1, 5) # 更小的半径(1-5像素) dot_positions.append((x, y, radius)) # 随机决定是否添加光圈 if random.random() < halo_prob: # 随机选择光圈强度 intensity_min, intensity_max = random.choice(halo_intensities) # 模拟真实的光圈效果 img = simulate_halo(img, (x, y), radius, (intensity_min, intensity_max)) # 绘制黑色圆点(永远在最上层) cv2.circle(img, (x, y), radius, (0, 0, 0), -1) # YOLO格式标签(归一化坐标) label_lines.append(f"0 {x/640:.6f} {y/640:.6f} {2*radius/640:.6f} {2*radius/640:.6f}") # 随机添加干扰元素(避免重叠) if random.random() > 0.3: # 70%概率添加干扰 for _ in range(random.randint(1, 5)): # 随机干扰类型(线条或斑点) if random.random() > 0.5: # 线条干扰 color = np.random.randint(max(0, bg_min-20), min(255, bg_max+20)) pt1 = (np.random.randint(0, 640), np.random.randint(0, 640)) # 确保第二点不会与圆点重叠 valid_pt = False attempts = 0 while not valid_pt and attempts < 10: pt2 = (np.random.randint(0, 640), np.random.randint(0, 640)) valid_pt = True for (dx, dy, dr) in dot_positions: dist = np.sqrt((pt2[0]-dx)**2 + (pt2[1]-dy)**2) if dist < dr + 10: # 保持安全距离 valid_pt = False break attempts += 1 cv2.line(img, pt1, pt2, (color, color, color), random.randint(1, 2)) else: # 斑点干扰 valid_spot = False attempts = 0 while not valid_spot and attempts < 10: spot_x = np.random.randint(0, 640) spot_y = np.random.randint(0, 640) spot_radius = np.random.randint(1, 3) # 检查是否与任何圆点重叠 overlap = False for (dx, dy, dr) in dot_positions: dist = np.sqrt((spot_x-dx)**2 + (spot_y-dy)**2) if dist < dr + spot_radius + 5: # 保持安全距离 overlap = True break if not overlap: valid_spot = True spot_color = np.random.randint(max(0, bg_min-20), min(255, bg_max+20)) cv2.circle(img, (spot_x, spot_y), spot_radius, (spot_color, spot_color, spot_color), -1) attempts += 1 # 随机添加额外的光圈干扰(没有黑色圆点) if random.random() < halo_prob * 0.3: # 30%的概率添加干扰光圈 num_false_halos = random.randint(1, 3) for _ in range(num_false_halos): halo_x = np.random.randint(20, 620) halo_y = np.random.randint(20, 620) halo_radius = random.randint(2, 8) intensity_min, intensity_max = random.choice(halo_intensities) img = simulate_halo(img, (halo_x, halo_y), halo_radius, (intensity_min, intensity_max)) # 添加负样本(非目标物体) num_negatives = random.randint(0, 8) # 随机数量的负样本 img, negative_shapes = add_negative_samples(img, dot_positions, num_negatives) # 保存图像和标签(只记录目标圆点) # ... [保持不变] ... img_path = output_path/f"images/train/img_{i:04d}.jpg" cv2.imwrite(str(img_path), img) label_path = output_path/f"labels/train/img_{i:04d}.txt" with open(label_path, "w") as f: f.write("\n".join(label_lines)) print(f"✅ 数据集创建完成: {len(list((output_path/'images/train').glob('*.jpg')))} 张图像") return output_path.resolve() # 创建可靠的YAML配置(不变) def create_dataset_config(data_dir): """创建绝对路径安全的YAML配置文件""" data_path = Path(data_dir) # 验证数据集结构 if not (data_path/"images/train").exists(): raise FileNotFoundError(f"数据集结构错误: 缺少 {data_path/'images/train'}") config = { 'path': str(data_path), 'train': 'images/train', 'val': 'images/train', # 简单验证使用相同数据 'names': {0: 'black_dot'}, 'download': False } yaml_path = data_path.parent / "black_dot_config.yaml" with open(yaml_path, 'w') as f: yaml.dump(config, f, sort_keys=False) print(f"⚙️ 配置文件创建于: {yaml_path}") return yaml_path # 增强的检测后处理函数 def postprocess_detections(results, frame, shape_threshold=0.7, color_threshold=0.1): """ 过滤误检测:基于形状和颜色特征 :param results: YOLO检测结果 :param frame: 原始图像帧 :param shape_threshold: 圆形度阈值(0-1) :param color_threshold: 颜色纯度阈值(0-1) :return: 过滤后的检测结果 """ filtered_boxes = [] filtered_confidences = [] filtered_classes = [] # 获取原始检测结果 boxes = results[0].boxes.xyxy.cpu().numpy() confidences = results[0].boxes.conf.cpu().numpy() classes = results[0].boxes.cls.cpu().numpy() # 将图像转换为HSV颜色空间进行颜色分析 hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) for i in range(len(boxes)): x1, y1, x2, y2 = boxes[i] conf = confidences[i] cls_id = classes[i] # 提取检测区域 roi = frame[int(y1):int(y2), int(x1):int(x2)] hsv_roi = hsv_frame[int(y1):int(y2), int(x1):int(x2)] # 计算形状特征(圆形度) if roi.size > 0: # 二值化处理 gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) # 计算轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: # 获取最大轮廓 max_contour = max(contours, key=cv2.contourArea) # 计算轮廓面积和凸包面积 contour_area = cv2.contourArea(max_contour) convex_hull = cv2.convexHull(max_contour) hull_area = cv2.contourArea(convex_hull) # 计算圆形度(圆形的圆形度接近1) circularity = (4 * np.pi * contour_area) / (cv2.arcLength(max_contour, True)**2) if contour_area > 0 else 0 # 计算凸性缺陷(凸包的缺陷越少越接近圆形) defects = cv2.convexityDefects(max_contour, cv2.convexHull(max_contour, returnPoints=False)) convexity = 1 - (len(defects) / 10) if defects is not None else 1.0 # 综合形状得分 shape_score = minmax_scale([(circularity + convexity) / 2])[0] # 计算颜色特征(黑色纯度) avg_color = np.mean(roi, axis=(0, 1)) blackness = 1 - np.mean(avg_color) / 255 # 计算颜色纯度(黑色物体应具有低饱和度) saturation = np.mean(hsv_roi[:, :, 1]) / 255 color_purity = minmax_scale([1 - saturation])[0] # 综合颜色得分 color_score = minmax_scale([(blackness + color_purity) / 2])[0] # 形状和颜色得分都高于阈值才保留 if shape_score > shape_threshold and color_score > color_threshold: filtered_boxes.append(boxes[i]) filtered_confidences.append(conf) filtered_classes.append(cls_id) # 更新结果对象 if filtered_boxes: results[0].boxes = type(results[0].boxes)( torch.tensor(np.array(filtered_boxes)), torch.tensor(np.array(filtered_confidences)), torch.tensor(np.array(filtered_classes)) ) else: results[0].boxes = type(results[0].boxes)(torch.empty((0, 6))) # 空结果 return results # 改进的训练函数(添加负样本增强) def train_model_safe(config_path): """改进的训练函数,使用负样本增强""" # ... [前面代码保持不变] ... config_path = Path(config_path) # 验证配置文件 if not config_path.exists(): raise FileNotFoundError(f"配置文件不存在: {config_path}") # 设置Ultralytics环境 os.environ['ULTRALYTICS_DATASETS'] = str(config_path.parent) print("🚀 开始训练YOLOv8模型...") print(f" 使用配置文件: {config_path}") try: # 加载模型 model = YOLO("yolov8n.pt") # 训练参数 - 添加针对光圈的增强 train_args = { # ... [其他参数保持不变] ... 'data': str(config_path), 'epochs': 1000, # 增加训练轮数 'imgsz': 640, 'batch': 8, 'device': '0' if torch.cuda.is_available() else 'cpu', 'project': "black_dot_detection", 'name': f"exp_{datetime.now().strftime('%Y%m%d_%H%M%S')}", 'exist_ok': True, 'single_cls': True, 'optimizer': 'Adam', 'lr0': 0.01, 'close_mosaic': 20, 'augment': True, # 启用增强 'degrees': 45.0, # 旋转角度范围 'translate': 0.2, # 平移范围 'scale': 0.8, # 缩放范围 'shear': 10.0, # 裁剪范围 'hsv_h': 0.015, # 色相增强 'hsv_s': 0.7, # 饱和度增强 'hsv_v': 0.4, # 明度增强 'flipud': 0.5, # 垂直翻转 'fliplr': 0.5, # 水平翻转 'mosaic': 1.0, # 使用马赛克增强 'mixup': 0.1, # 使用mixup增强 'copy_paste': 0.1, # 使用复制粘贴增强 'erasing': 0.4, # 随机擦除 'auto_augment': 'randaugment', # 自动增强 } # 启动训练 results = model.train(**train_args) # 获取最佳模型路径 model_dir = Path(results.save_dir) / "weights" / "best.pt" print(f"🎉 训练完成! 最佳模型保存于: {model_dir}") return model_dir except Exception as e: print(f"❌ 训练失败: {str(e)}") print("排查建议:") print("1. 检查配置文件路径是否正确") print("2. 确保数据集包含至少100张图像") print("3. 尝试降低batch_size值") print("4. 更新ultralytics包: pip install --upgrade ultralytics") sys.exit(1) # 主程序 if __name__ == "__main__": # ... [主程序代码保持不变] ... print("="*50) print("基于YOLOv8的黑色圆点检测系统(抗光圈干扰版)") print("="*50) # 1. 路径安全检查 # (validate_system_paths函数保持不变) # 2. 创建数据集目录在当前脚本所在目录 script_dir = Path(__file__).parent.resolve() dataset_dir = script_dir / "black_dot_dataset" # 3. 创建改进的数据集 if not dataset_dir.exists(): print("\n[阶段1] 创建抗光圈干扰的训练数据集") dataset_dir = create_robust_dataset(dataset_dir, halo_prob=0.7) else: print(f"\n[阶段1] 使用现有数据集: {dataset_dir}") # 4. 创建配置文件 print("\n[阶段2] 创建数据集配置") config_path = create_dataset_config(dataset_dir) # 5. 训练改进的模型 print("\n[阶段3] 训练抗光圈干扰模型") model_path = train_model_safe(config_path) print("\n" + "="*50) print(f"处理完成! 最佳模型保存于: {model_path}") print("="*50) 阅读代码,改代码能识别较模糊的目标吗
最新发布
10-27
import os import sys import cv2 import torch import yaml import numpy as np from pathlib import Path from ultralytics import YOLO from datetime import datetime # 系统路径安全检测(不变) def validate_system_paths(): """检查系统路径中是否包含非法字符""" critical_chars = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', ';', ':', '"', '<', '>', '?', '`', '|', '=', '+'] env_vars = ['PYTHONPATH', 'PROGRAMDATA', 'APPDATA', 'USERPROFILE', 'HOMEPATH'] errors = [] for var in env_vars: path = os.environ.get(var, '') if any(char in path for char in critical_chars): errors.append(f"环境变量 {var} 包含非法字符: {path}") if errors: print("⚠️ 路径安全警告:") for error in errors: print(error) print("建议: 将项目移动到不含特殊字符的路径(如 D:/projects/)") return False return True # 修改后的数据集创建函数 def create_robust_dataset(output_dir="black_dot_dataset"): """创建带有多样性背景的模拟数据集(已修改)""" output_path = Path(output_dir) # 创建目录结构 (output_path/"images/train").mkdir(parents=True, exist_ok=True) (output_path/"labels/train").mkdir(parents=True, exist_ok=True) print(f"🔧 创建数据集于: {output_path.resolve()}") for i in range(1000): # 1000张训练图像 # 创建灰色背景(70-100灰度值) base_gray = np.random.randint(70, 100) img = np.full((640, 640, 3), base_gray, dtype=np.uint8) # 添加轻微高斯噪声增强真实性 noise = np.random.normal(0, 3, (640, 640, 3)).astype(np.uint8) img = cv2.add(img, noise) num_dots = np.random.randint(3, 15) # 增加圆点数量变化 label_lines = [] for dot_idx in range(num_dots): # 随机位置和大小(更小的圆点) x = np.random.randint(20, 620) y = np.random.randint(20, 620) radius = np.random.randint(1, 5) # 更小的半径(1-5像素) # 40%的概率添加白色光环效果 if np.random.random() < 0.4: # 创建光环参数 halo_radius = radius + np.random.randint(3, 8) # 光环比圆点大3-8像素 halo_blur = np.random.randint(1, 3) # 轻微模糊效果 # 创建光环(白色圆形) halo_img = np.zeros_like(img) cv2.circle(halo_img, (x, y), halo_radius, (255, 255, 255), -1) # 应用高斯模糊使光环边缘柔和 halo_img = cv2.GaussianBlur(halo_img, (0, 0), halo_blur) # 将光环叠加到原图 img = cv2.addWeighted(img, 1, halo_img, 0.7, 0) # 绘制黑色圆点(永远在最上层) cv2.circle(img, (x, y), radius, (0, 0, 0), -1) # YOLO格式标签(归一化坐标) label_lines.append(f"0 {x/640:.6f} {y/640:.6f} {2*radius/640:.6f} {2*radius/640:.6f}") # 随机添加干扰元素 if np.random.random() > 0.3: # 70%概率添加干扰 for _ in range(np.random.randint(1, 5)): # 随机干扰类型(线条或斑点) if np.random.random() > 0.5: # 线条干扰 color = np.random.randint(50, 150) pt1 = (np.random.randint(0, 640), np.random.randint(0, 640)) pt2 = (np.random.randint(0, 640), np.random.randint(0, 640)) cv2.line(img, pt1, pt2, (color, color, color), np.random.randint(1, 2)) else: # 斑点干扰 spot_x = np.random.randint(0, 640) spot_y = np.random.randint(0, 640) spot_radius = np.random.randint(1, 3) spot_color = np.random.randint(70, 130) cv2.circle(img, (spot_x, spot_y), spot_radius, (spot_color, spot_color, spot_color), -1) # 保存图像和标签 img_path = output_path/f"images/train/img_{i:04d}.jpg" cv2.imwrite(str(img_path), img) label_path = output_path/f"labels/train/img_{i:04d}.txt" with open(label_path, "w") as f: f.write("\n".join(label_lines)) print(f"✅ 数据集创建完成: {len(list((output_path/'images/train').glob('*.jpg')))} 张图像") return output_path.resolve() # 创建可靠的YAML配置(不变) def create_dataset_config(data_dir): """创建绝对路径安全的YAML配置文件""" data_path = Path(data_dir) # 验证数据集结构 if not (data_path/"images/train").exists(): raise FileNotFoundError(f"数据集结构错误: 缺少 {data_path/'images/train'}") config = { 'path': str(data_path), 'train': 'images/train', 'val': 'images/train', # 简单验证使用相同数据 'names': {0: 'black_dot'}, 'download': False } yaml_path = data_path.parent / "black_dot_config.yaml" with open(yaml_path, 'w') as f: yaml.dump(config, f, sort_keys=False) print(f"⚙️ 配置文件创建于: {yaml_path}") return yaml_path # 稳健的训练函数(不变) def train_model_safe(config_path): """带异常处理的训练函数""" config_path = Path(config_path) # 验证配置文件 if not config_path.exists(): raise FileNotFoundError(f"配置文件不存在: {config_path}") # 设置Ultralytics环境 os.environ['ULTRALYTICS_DATASETS'] = str(config_path.parent) print("🚀 开始训练YOLOv8模型...") print(f" 使用配置文件: {config_path}") try: # 加载模型 model = YOLO("yolov8n.pt") # 训练参数 train_args = { 'data': str(config_path), 'epochs': 100, 'imgsz': 640, 'batch': 8, 'device': '0' if torch.cuda.is_available() else 'cpu', 'project': "black_dot_detection", 'name': f"exp_{datetime.now().strftime('%Y%m%d_%H%M%S')}", 'exist_ok': True, 'single_cls': True, 'optimizer': 'Adam', 'lr0': 0.01, 'close_mosaic': 20 } # 启动训练 results = model.train(**train_args) # 获取最佳模型路径 model_dir = Path(results.save_dir) / "weights" / "best.pt" print(f"🎉 训练完成! 最佳模型保存于: {model_dir}") return model_dir except Exception as e: print(f"❌ 训练失败: {str(e)}") print("排查建议:") print("1. 检查配置文件路径是否正确") print("2. 确保数据集包含至少10张图像") print("3. 尝试降低batch_size值") print("4. 更新ultralytics包: pip install --upgrade ultralytics") sys.exit(1) # 主程序(不变) if __name__ == "__main__": print("="*50) print("基于YOLOv8的黑色圆点检测系统") print("="*50) # 1. 路径安全检查 if not validate_system_paths(): print("警告: 路径问题可能导致训练失败,建议修改项目位置") # 2. 创建数据集目录在当前脚本所在目录 script_dir = Path(__file__).parent.resolve() dataset_dir = script_dir / "black_dot_dataset" # 3. 创建数据集(如果不存在) if not dataset_dir.exists(): print("\n[阶段1] 创建训练数据集") dataset_dir = create_robust_dataset(dataset_dir) else: print(f"\n[阶段1] 使用现有数据集: {dataset_dir}") # 4. 创建配置文件 print("\n[阶段2] 创建数据集配置") config_path = create_dataset_config(dataset_dir) # 5. 训练模型 print("\n[阶段3] 训练检测模型") model_path = train_model_safe(config_path) print("\n" + "="*50) print(f"处理完成! 最佳模型保存于: {model_path}") print("="*50) 该代码训练的模型不能消除白色明亮光圈的影响
10-12
import os import sys import cv2 import torch import yaml import numpy as np from pathlib import Path from ultralytics import YOLO from datetime import datetime import random from skimage.util import random_noise # 系统路径安全检测 def validate_system_paths(): """检查系统路径中是否包含非法字符""" critical_chars = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', ';', ':', '"', '<', '>', '?', '`', '|', '=', '+'] # 获取所有可能影响路径的环境变量 env_vars = ['PYTHONPATH', 'PROGRAMDATA', 'APPDATA', 'USERPROFILE', 'HOMEPATH'] errors = [] for var in env_vars: path = os.environ.get(var, '') if any(char in path for char in critical_chars): errors.append(f"环境变量 {var} 包含非法字符: {path}") if errors: print("⚠️ 路径安全警告:") for error in errors: print(error) print("建议: 将项目移动到不含特殊字符的路径(如 D:/projects/)") return False return True # 创建更真实的模拟数据集(针对小圆点、灰色背景和白色光圈干扰) def create_realistic_dataset(output_dir="black_dot_dataset_realistic"): """创建符合实际场景的模拟数据集""" output_path = Path(output_dir) # 创建目录结构 (output_path/"images/train").mkdir(parents=True, exist_ok=True) (output_path/"labels/train").mkdir(parents=True, exist_ok=True) # 灰色背景色调库 (70-180的灰色范围) backgrounds = [ # 均匀灰色背景 lambda h, w: np.full((h, w, 3), np.random.randint(70, 180), dtype=np.uint8), # 渐变灰色背景 lambda h, w: np.linspace( np.random.randint(70, 120), np.random.randint(130, 180), w ).astype(np.uint8)[np.newaxis, :].repeat(h, axis=0)[..., np.newaxis].repeat(3, axis=2), # 带纹理的灰色背景 lambda h, w: cv2.blur( np.random.randint(50, 200, (h, w, 3), dtype=np.uint8), (30, 30) ), # 带噪点的灰色背景 lambda h, w: (random_noise( np.full((h, w, 3), np.random.randint(90, 160), dtype=np.uint8)/255.0, mode='gaussian', var=0.005 ) * 255).astype(np.uint8) ] print(f"🔧 创建真实场景数据集于: {output_path.resolve()}") for i in range(150): # 增加到150张图像以增强多样性 # 随机选择背景 bg_func = np.random.choice(backgrounds) img = bg_func(640, 640) # 添加真实感干扰元素 # 1. 随机添加白色光圈干扰 num_halos = np.random.randint(2, 6) for _ in range(num_halos): x = np.random.randint(50, 590) y = np.random.randint(50, 590) radius = np.random.randint(5, 15) thickness = np.random.randint(1, 3) # 创建白色光圈(圆环) cv2.circle(img, (x, y), radius, (220, 220, 220), thickness) # 可选:添加内部半透明填充 if np.random.random() > 0.7: radius_inner = max(1, radius - thickness - 1) overlay = img.copy() cv2.circle(overlay, (x, y), radius_inner, (240, 240, 240), -1) alpha = np.random.uniform(0.1, 0.4) img = cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0) # 2. 随机添加噪点干扰 if np.random.random() > 0.3: noise_mask = (random_noise(np.zeros_like(img), mode='s&p', amount=0.005) * 255).astype(np.uint8) img = cv2.addWeighted(img, 0.9, noise_mask, 0.1, 0) num_dots = np.random.randint(5, 15) # 增加圆点数量 label_lines = [] for _ in range(num_dots): # 随机位置和小尺寸(2-5像素) x = np.random.randint(20, 620) y = np.random.randint(20, 620) radius = np.random.randint(1, 4) # 小圆点尺寸 # 更深的黑色(0-20灰度值) darkness = np.random.randint(0, 20) dot_color = (darkness, darkness, darkness) # 创建带光晕效果的小黑点 overlay = img.copy() # 先绘制光晕(白色半透明) if np.random.random() > 0.3: # 70%的圆点添加光晕干扰 halo_radius = radius + np.random.randint(1, 3) cv2.circle(overlay, (x, y), halo_radius, (220, 220, 220), -1) alpha = np.random.uniform(0.2, 0.5) img = cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0) # 再绘制黑色圆点 cv2.circle(img, (x, y), radius, dot_color, -1) # YOLO格式标签(边界框覆盖整个光晕效果) label_radius = halo_radius if 'halo_radius' in locals() else radius label_lines.append(f"0 {x/640:.6f} {y/640:.6f} {2*label_radius/640:.6f} {2*label_radius/640:.6f}") # 保存图像和标签 img_path = output_path/f"images/train/img_{i:04d}.jpg" cv2.imwrite(str(img_path), img) label_path = output_path/f"labels/train/img_{i:04d}.txt" with open(label_path, "w") as f: f.write("\n".join(label_lines)) # 进度显示 if (i+1) % 10 == 0: print(f"已生成 {i+1}/150 张图像", end='\r') print(f"\n✅ 真实场景数据集创建完成: {len(list((output_path/'images/train').glob('*.jpg')))} 张图像") return output_path.resolve() # 创建可靠的YAML配置 def create_dataset_config(data_dir): """创建绝对路径安全的YAML配置文件""" data_path = Path(data_dir) # 验证数据集结构 if not (data_path/"images/train").exists(): raise FileNotFoundError(f"数据集结构错误: 缺少 {data_path/'images/train'}") config = { 'path': str(data_path), 'train': 'images/train', 'val': 'images/train', # 简单验证使用相同数据 'names': {0: 'black_dot'}, 'download': False, 'augment': True # 启用内置数据增强 } yaml_path = data_path.parent / "black_dot_realistic_config.yaml" with open(yaml_path, 'w') as f: yaml.dump(config, f, sort_keys=False) print(f"⚙️ 配置文件创建于: {yaml_path}") return yaml_path # 优化训练函数(针对小目标检测) def train_model_optimized(config_path): """针对小目标优化的训练函数""" config_path = Path(config_path) # 验证配置文件 if not config_path.exists(): raise FileNotFoundError(f"配置文件不存在: {config_path}") # 设置Ultralytics环境 os.environ['ULTRALYTICS_DATASETS'] = str(config_path.parent) print("🚀 开始训练优化版YOLOv8模型...") print(f" 使用配置文件: {config_path}") try: # 加载模型 - 使用针对小目标优化的YOLOv8s模型 model = YOLO("yolov8s.pt") # 优化的训练参数 train_args = { 'data': str(config_path), 'epochs': 200, # 增加训练轮次 'imgsz': 640, 'batch': 4 if torch.cuda.is_available() else 2, # 减小批次大小以增加分辨率 'device': '0' if torch.cuda.is_available() else 'cpu', 'project': "black_dot_detection_realistic", 'name': f"exp_{datetime.now().strftime('%Y%m%d_%H%M%S')}", 'exist_ok': True, 'single_cls': True, 'optimizer': 'AdamW', # 使用改进的优化器 'lr0': 0.01, 'close_mosaic': 10, # 更早关闭马赛克增强 'fliplr': 0.0, # 禁用水平翻转(可能改变目标特征) 'hsv_h': 0.015, # 减少色相变化(保持灰色背景) 'hsv_s': 0.2, # 减少饱和度变化 'hsv_v': 0.2, # 减少明度变化 'degrees': 5.0, # 减少旋转角度 'scale': 0.1, # 减少缩放幅度 'mixup': 0.0, # 禁用mixup增强 'copy_paste': 0.0, # 禁用复制粘贴增强 'box': 2.0, # 增加box loss权重 'cls': 0.5, # 减少cls loss权重(单类别) 'dfl': 1.0, # 分布焦点损失 'fl_gamma': 1.5, # 焦点损失gamma参数 'label_smoothing': 0.05, # 标签平滑 'noplots': False, # 启用训练图表 'pretrained': True } # 启动训练 results = model.train(**train_args) # 获取最佳模型路径 model_dir = Path(results.save_dir) / "weights" / "best.pt" print(f"🎉 训练完成! 最佳模型保存于: {model_dir}") return model_dir except Exception as e: print(f"❌ 训练失败: {str(e)}") print("排查建议:") print("1. 检查显存是否充足,尝试减小batch_size") print("2. 确保数据集包含足够的样本") print("3. 尝试使用更小的模型: yolov8n.pt") print("4. 更新ultralytics包: pip install --upgrade ultralytics") sys.exit(1) # 主程序 if __name__ == "__main__": print("="*50) print("基于YOLOv8的复杂场景小黑圆点检测系统") print("="*50) # 1. 路径安全检查 if not validate_system_paths(): print("警告: 路径问题可能导致训练失败,建议修改项目位置") # 2. 创建数据集目录在当前脚本所在目录 script_dir = Path(__file__).parent.resolve() dataset_dir = script_dir / "black_dot_dataset_realistic" # 3. 创建真实场景数据集 print("\n[阶段1] 创建真实场景训练数据集") dataset_dir = create_realistic_dataset(dataset_dir) # 4. 创建配置文件 print("\n[阶段2] 创建数据集配置") config_path = create_dataset_config(dataset_dir) # 5. 训练优化模型 print("\n[阶段3] 训练优化检测模型") model_path = train_model_optimized(config_path) print("\n" + "="*50) print(f"处理完成! 最佳模型保存于: {model_path}") print("="*50) 显存只有4GB,修改一下
10-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值