欢迎光临
我们一直在努力

光伏电池板语义分割系统 光伏电池板语义分割数据集

使用深度学习模型训练使用/光电池板语义分割数据集在这里插入图片描述

共包含3个数据文件:
/train:训练集
/valid:验证集
/test:测试集
在这里插入图片描述

检测目标以Pascal VOC格式进行标注,对每个图像进行以下预处理,未做统一尺寸的处理。数据集共包含9614分布式光伏图像样张,其中train包含8499图像样张及其对应的xml标注,valid包含674图像样张及其对应的xml标注,test包含441图像样张及其对应的xml标注在这里插入图片描述
构建一个光伏电池板语义分割系统,我们需要处理包含9614张图像的数据集,并且这些图像使用Pascal VOC格式进行标注。我们将使用深度学习模型来进行语义分割任务。以下是详细的步骤和代码实现:

文章代码仅供参考、

  • 数据准备:确保数据集格式正确。
  • 环境部署:安装必要的库。
  • 数据预处理:对图像进行归一化等预处理操作。
  • 模型定义:定义用于语义分割的深度学习模型(例如 UNet 或 DeepLabV3+)。
  • 训练模型:编写训练脚本。
  • 评估模型:编写评估脚本。
  • 用户界面:创建带有和不带 UI 的预测脚本。
  • 数据准备

    假设你已经有一个包含9614张遥感图像的数据集,并且标注格式为适合语义分割任务的格式(例如,Pascal VOC 格式)。

    数据集结构示例

    dataset/
    ├── images/
    │ ├── train/
    │ │ ├── image1.jpg
    │ │ ├── image2.jpg
    │ │ └── …
    │ ├── valid/
    │ │ ├── image3.jpg
    │ │ ├── image4.jpg
    │ │ └── …
    │ └── test/
    │ ├── image5.jpg
    │ ├── image6.jpg
    │ └── …
    ├── annotations/
    │ ├── train/
    │ │ ├── image1.xml
    │ │ ├── image2.xml
    │ │ └── …
    │ ├── valid/
    │ │ ├── image3.xml
    │ │ ├── image4.xml
    │ │ └── …
    │ └── test/
    │ ├── image5.xml
    │ ├── image6.xml
    │ └── …
    └── dataset.json

    dataset.json 内容如下:

    {
    "train": {
    "img_dir": "./images/train",
    "ann_dir": "./annotations/train"
    },
    "valid": {
    "img_dir": "./images/valid",
    "ann_dir": "./annotations/valid"
    },
    "test": {
    "img_dir": "./images/test",
    "ann_dir": "./annotations/test"
    }
    }

    环境部署说明

    确保你已经安装了必要的库,如上所述。

    安装依赖

    # 创建虚拟环境(可选)
    conda create -n pv_panel_segmentation_env python=3.8
    conda activate pv_panel_segmentation_env

    # 安装PyTorch
    pip install torch==1.9 torchvision torchaudio –extra-index-url https://download.pytorch.org/whl/cu111

    # 安装其他依赖
    pip install opencv-python pyqt5 scikit-learn pandas matplotlib seaborn onnxruntime xml.etree.ElementTree albumentations segmentation-models-pytorch

    数据预处理 data_preprocessing.py

    我们将使用 albumentations 进行图像预处理。

    [<title="Data Preprocessing for PV Panel Segmentation">]
    import os
    import cv2
    import numpy as np
    from tqdm import tqdm
    from albumentations import (
    HorizontalFlip, VerticalFlip, RandomRotate90, Transpose,
    ShiftScaleRotate, HueSaturationValue, Blur, OpticalDistortion,
    GridDistortion, CLAHE, RandomBrightnessContrast, RandomGamma,
    ToFloat, Normalize, Compose, NoOp, OneOf, Resize
    )
    from albumentations.pytorch import ToTensorV2
    import xml.etree.ElementTree as ET
    import shutil

    def parse_voc_xml(xml_path):
    tree = ET.parse(xml_path)
    root = tree.getroot()
    size = root.find('size')
    width = int(size.find('width').text)
    height = int(size.find('height').text)

    masks = []
    labels = []

    for obj in root.findall('object'):
    label = obj.find('name').text
    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)

    mask = np.zeros((height, width), dtype=np.uint8)
    mask[ymin:ymax, xmin:xmax] = 1
    masks.append(mask)
    labels.append(label)

    return masks, labels

    def preprocess_image(image_path, xml_path, output_img_path, output_mask_paths, patch_size=512):
    image = cv2.imread(image_path)
    masks, _ = parse_voc_xml(xml_path)

    transform = A.Compose([
    Resize(height=patch_size, width=patch_size),
    HorizontalFlip(p=0.5),
    VerticalFlip(p=0.5),
    Rotate(limit=45, p=0.5),
    RandomBrightnessContrast(p=0.2),
    ToFloat(max_value=255),
    Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
    ])

    transformed = transform(image=image)
    image_transformed = transformed['image']

    output_img_path = f"{output_img_path}.jpg"

    cv2.imwrite(output_img_path, (image_transformed.permute(1, 2, 0).numpy() * 255).astype(np.uint8))

    for i, mask in enumerate(masks):
    mask_resized = cv2.resize(mask.astype(np.float32), (patch_size, patch_size))
    output_mask_path = f"{output_mask_paths[i]}_{i}.png"
    cv2.imwrite(output_mask_path, (mask_resized * 255).astype(np.uint8))

    if __name__ == "__main__":
    dataset_path = 'path/to/dataset'
    output_dataset_path = 'path/to/output_dataset'

    sets = ['train', 'valid', 'test']
    for s in sets:
    img_dir = os.path.join(dataset_path, 'images', s)
    ann_dir = os.path.join(dataset_path, 'annotations', s)
    output_img_dir = os.path.join(output_dataset_path, 'images', s)
    output_mask_dir = os.path.join(output_dataset_path, 'masks', s)

    os.makedirs(output_img_dir, exist_ok=True)
    os.makedirs(output_mask_dir, exist_ok=True)

    for img_filename in tqdm(os.listdir(img_dir)):
    img_path = os.path.join(img_dir, img_filename)
    xml_filename = img_filename.replace('.jpg', '.xml')
    xml_path = os.path.join(ann_dir, xml_filename)
    output_img_path = os.path.join(output_img_dir, img_filename[:–4])
    output_mask_paths = [os.path.join(output_mask_dir, img_filename[:–4])]

    preprocess_image(img_path, xml_path, output_img_path, output_mask_paths)

    请将 path/to/dataset 和 path/to/output_dataset 替换为实际的数据集路径。

    模型定义与训练 train.py

    我们将使用 UNet 进行语义分割任务。

    首先,安装 segmentation-models-pytorch 库:

    pip install segmentation-models-pytorch

    然后编写训练脚本:

    [<title="Training Script for PV Panel Segmentation using UNet">]
    import os
    import json
    import torch
    from torch.utils.data import DataLoader
    from torchvision.transforms.functional import to_tensor, to_pil_image
    from PIL import Image
    import albumentations as A
    from albumentations.pytorch import ToTensorV2
    from segmentation_models_pytorch import Unet
    from sklearn.model_selection import train_test_split
    import matplotlib.pyplot as plt
    import numpy as np

    class PVDataset(torch.utils.data.Dataset):
    def __init__(self, img_dir, mask_dir, transforms=None):
    self.img_dir = img_dir
    self.mask_dir = mask_dir
    self.transforms = transforms
    self.images = sorted(os.listdir(img_dir))
    self.masks = sorted(os.listdir(mask_dir))

    def __len__(self):
    return len(self.images)

    def __getitem__(self, idx):
    img_path = os.path.join(self.img_dir, self.images[idx])
    mask_path = os.path.join(self.mask_dir, self.masks[idx])

    image = Image.open(img_path).convert("RGB")
    mask = Image.open(mask_path).convert("L")

    sample = {'image': image, 'mask': mask}

    if self.transforms:
    augmented = self.transforms(image=np.array(sample['image']), mask=np.array(sample['mask']))
    image = augmented['image']
    mask = augmented['mask']

    return image, mask

    transform = A.Compose([
    A.Resize(height=256, width=256),
    A.HorizontalFlip(p=0.5),
    A.VerticalFlip(p=0.5),
    A.Rotate(limit=45, p=0.5),
    A.RandomBrightnessContrast(p=0.2),
    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
    ])

    with open('path/to/dataset.json', 'r') as f:
    dataset_config = json.load(f)

    train_dataset = PVDataset(
    img_dir=dataset_config['train']['img_dir'],
    mask_dir=dataset_config['train']['ann_dir'],
    transforms=transform
    )

    valid_dataset = PVDataset(
    img_dir=dataset_config['valid']['img_dir'],
    mask_dir=dataset_config['valid']['ann_dir'],
    transforms=transform
    )

    train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=4)
    valid_loader = DataLoader(valid_dataset, batch_size=16, shuffle=False, num_workers=4)

    model = Unet(encoder_name="resnet34", encoder_weights="imagenet", classes=1, activation=None)

    loss_fn = torch.nn.BCEWithLogitsLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)

    num_epochs = 50

    for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    for inputs, targets in train_loader:
    inputs = inputs.to(device)
    targets = targets.unsqueeze(1).to(device).float()

    optimizer.zero_grad()

    outputs = model(inputs)
    loss = loss_fn(outputs, targets)
    loss.backward()
    optimizer.step()

    running_loss += loss.item()

    epoch_loss = running_loss / len(train_loader)
    print(f"Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.4f}")

    model.eval()
    val_running_loss = 0.0
    with torch.no_grad():
    for inputs, targets in valid_loader:
    inputs = inputs.to(device)
    targets = targets.unsqueeze(1).to(device).float()

    outputs = model(inputs)
    loss = loss_fn(outputs, targets)

    val_running_loss += loss.item()

    val_epoch_loss = val_running_loss / len(valid_loader)
    print(f"Validation Epoch {epoch + 1}/{num_epochs}, Loss: {val_epoch_loss:.4f}")

    torch.save(model.state_dict(), f'model_epoch_{epoch + 1}.pth')

    请将 path/to/dataset.json 替换为实际的路径。

    评估模型 evaluate.py

    [<title="Evaluation Script for PV Panel Segmentation">]
    import os
    import json
    import torch
    from torch.utils.data import DataLoader
    from torchvision.transforms.functional import to_tensor, to_pil_image
    from PIL import Image
    import albumentations as A
    from albumentations.pytorch import ToTensorV2
    from segmentation_models_pytorch import Unet
    import numpy as np
    import matplotlib.pyplot as plt

    class PVDataset(torch.utils.data.Dataset):
    def __init__(self, img_dir, mask_dir, transforms=None):
    self.img_dir = img_dir
    self.mask_dir = mask_dir
    self.transforms = transforms
    self.images = sorted(os.listdir(img_dir))
    self.masks = sorted(os.listdir(mask_dir))

    def __len__(self):
    return len(self.images)

    def __getitem__(self, idx):
    img_path = os.path.join(self.img_dir, self.images[idx])
    mask_path = os.path.join(self.mask_dir, self.masks[idx])

    image = Image.open(img_path).convert("RGB")
    mask = Image.open(mask_path).convert("L")

    sample = {'image': image, 'mask': mask}

    if self.transforms:
    augmented = self.transforms(image=np.array(sample['image']), mask=np.array(sample['mask']))
    image = augmented['image']
    mask = augmented['mask']

    return image, mask

    transform = A.Compose([
    A.Resize(height=256, width=256),
    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
    ])

    with open('path/to/dataset.json', 'r') as f:
    dataset_config = json.load(f)

    test_dataset = PVDataset(
    img_dir=dataset_config['test']['img_dir'],
    mask_dir=dataset_config['test']['ann_dir'],
    transforms=transform
    )

    test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=4)

    model = Unet(encoder_name="resnet34", encoder_weights="imagenet", classes=1, activation=None)
    model.load_state_dict(torch.load('model_epoch_50.pth'))
    model.eval()

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)

    loss_fn = torch.nn.BCEWithLogitsLoss()

    running_loss = 0.0
    for inputs, targets in test_loader:
    inputs = inputs.to(device)
    targets = targets.unsqueeze(1).to(device).float()

    outputs = model(inputs)
    loss = loss_fn(outputs, targets)

    running_loss += loss.item()

    # Visualization
    input_np = inputs.squeeze().permute(1, 2, 0).cpu().numpy()
    target_np = targets.squeeze().cpu().numpy()
    output_np = torch.sigmoid(outputs).squeeze().cpu().numpy()

    fig, axes = plt.subplots(1, 3, figsize=(15, 5))
    axes[0].imshow(input_np)
    axes[0].set_title('Input Image')
    axes[0].axis('off')

    axes[1].imshow(target_np, cmap='gray')
    axes[1].set_title('Ground Truth Mask')
    axes[1].axis('off')

    axes[2].imshow(output_np > 0.5, cmap='gray')
    axes[2].set_title('Predicted Mask')
    axes[2].axis('off')

    plt.show()

    epoch_loss = running_loss / len(test_loader)
    print(f"Test Loss: {epoch_loss:.4f}")

    请将 path/to/dataset.json 替换为实际的路径。

    用户界面 ui.py

    我们将使用 PyQt5 创建一个简单的 GUI 来加载和运行模型进行实时预测。

    [<title="PyQt5 Main Window for PV Panel Segmentation">]
    import sys
    import cv2
    import numpy as np
    from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget, QFileDialog
    from PyQt5.QtGui import QImage, QPixmap
    from PyQt5.QtCore import Qt, QTimer
    import torch
    from segmentation_models_pytorch import Unet
    import albumentations as A
    from albumentations.pytorch import ToTensorV2
    from PIL import Image

    class MainWindow(QMainWindow):
    def __init__(self):
    super().__init__()

    self.setWindowTitle("PV Panel Segmentation System")
    self.setGeometry(100, 100, 800, 600)

    # Load UNet model
    self.model = Unet(encoder_name="resnet34", encoder_weights="imagenet", classes=1, activation=None)
    self.model.load_state_dict(torch.load('/path/to/model_epoch_50.pth'))
    self.model.eval()

    self.transform = A.Compose([
    A.Resize(height=256, width=256),
    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
    ])

    self.initUI()

    def initUI(self):
    self.central_widget = QWidget()
    self.setCentralWidget(self.central_widget)

    self.layout = QVBoxLayout()

    self.image_label = QLabel(self)
    self.image_label.setAlignment(Qt.AlignCenter)
    self.layout.addWidget(self.image_label)

    self.load_image_button = QPushButton("Load Image", self)
    self.load_image_button.clicked.connect(self.load_image)
    self.layout.addWidget(self.load_image_button)

    self.start_prediction_button = QPushButton("Start Prediction", self)
    self.start_prediction_button.clicked.connect(self.start_prediction)
    self.layout.addWidget(self.start_prediction_button)

    self.stop_prediction_button = QPushButton("Stop Prediction", self)
    self.stop_prediction_button.clicked.connect(self.stop_prediction)
    self.layout.addWidget(self.stop_prediction_button)

    self.central_widget.setLayout(self.layout)

    self.image_path = None
    self.timer = QTimer()
    self.timer.timeout.connect(self.update_frame)

    def load_image(self):
    options = QFileDialog.Options()
    file_name, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "", "Images (*.jpg *.jpeg *.png);;All Files (*)", options=options)
    if file_name:
    self.image_path = file_name
    self.display_image(file_name)

    def display_image(self, path):
    pixmap = QPixmap(path)
    scaled_pixmap = pixmap.scaled(self.image_label.width(), self.image_label.height(), Qt.KeepAspectRatio)
    self.image_label.setPixmap(scaled_pixmap)

    def start_prediction(self):
    if self.image_path is not None and not self.timer.isActive():
    self.timer.start(30) # Update frame every 30 ms

    def stop_prediction(self):
    if self.timer.isActive():
    self.timer.stop()
    self.image_label.clear()

    def update_frame(self):
    frame = cv2.imread(self.image_path)
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    augmented = self.transform(image=frame_rgb)
    image = augmented['image'].unsqueeze(0)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    image = image.to(device)

    with torch.no_grad():
    output = self.model(image)
    pred_mask = torch.sigmoid(output).squeeze().cpu().numpy()

    pred_mask = (pred_mask > 0.5).astype(np.uint8) * 255

    contours, _ = cv2.findContours(pred_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    h, w, ch = rgb_image.shape
    bytes_per_line = ch * w
    qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
    pixmap = QPixmap.fromImage(qt_image)
    scaled_pixmap = pixmap.scaled(self.image_label.width(), self.image_label.height(), Qt.KeepAspectRatio)
    self.image_label.setPixmap(scaled_pixmap)

    if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

    请将 /path/to/model_epoch_50.pth 替换为实际的模型权重路径。

    不带用户界面的预测脚本 without_ui.py

    [<title="Prediction Script without UI for PV Panel Segmentation">]
    import cv2
    import numpy as np
    import torch
    from segmentation_models_pytorch import Unet
    import albumentations as A
    from albumentations.pytorch import ToTensorV2
    from PIL import Image

    # Load UNet model
    model = Unet(encoder_name="resnet34", encoder_weights="imagenet", classes=1, activation=None)
    model.load_state_dict(torch.load('/path/to/model_epoch_50.pth'))
    model.eval()

    transform = A.Compose([
    A.Resize(height=256, width=256),
    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
    ToTensorV2(),
    ])

    def predict(image_path):
    frame = cv2.imread(image_path)
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    augmented = transform(image=frame_rgb)
    image = augmented['image'].unsqueeze(0)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    image = image.to(device)

    with torch.no_grad():
    output = model(image)
    pred_mask = torch.sigmoid(output).squeeze().cpu().numpy()

    pred_mask = (pred_mask > 0.5).astype(np.uint8) * 255

    contours, _ = cv2.findContours(pred_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow('Detected Image', frame)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    if __name__ == "__main__":
    image_path = 'path/to/image.jpg'
    predict(image_path)

    请将 /path/to/model_epoch_50.pth 和 path/to/image.jpg 替换为实际的路径。

    辅助工具 tools/check_img.py

    [<title="Check Image Files Tool">]
    import os

    def check_image_files(directory):
    supported_extensions = ['.jpg', '.jpeg', '.png']
    for filename in os.listdir(directory):
    if any(filename.lower().endswith(ext) for ext in supported_extensions):
    continue
    else:
    raise ValueError(f"Unsupported file format: {filename}")
    print("All image files are valid.")

    辅助工具 tools/check_ann.py

    [<title="Check Annotation Files Tool">]
    import os

    def check_annotation_files(directory, annotation_format='voc'):
    if annotation_format == 'voc':
    supported_extensions = ['.xml']
    else:
    raise ValueError("Unsupported annotation format. Use 'voc'.")

    for filename in os.listdir(directory):
    if any(filename.lower().endswith(ext) for ext in supported_extensions):
    continue
    else:
    raise ValueError(f"Unsupported file format: {filename}")
    print("All annotation files are valid.")

    使用说明

  • 配置路径:

    • 将 /path/to/dataset 设置为存放原始数据集的目录路径。
    • 将 /path/to/output_dataset 设置为存放预处理后数据集的目录路径。
    • 确保 /path/to/model_epoch_50.pth 是训练好的 UNet 模型权重路径。
    • 将 path/to/image.jpg 设置为要预测的图像路径。
  • 运行脚本:

    • 在终端中运行 data_preprocessing.py 脚本来预处理图像。
    • 在终端中运行 train.py 脚本来训练模型。
    • 在终端中运行 evaluate.py 来评估模型性能。
    • 在终端中运行 ui.py 来启动 GUI 应用程序。
    • 在终端中运行 without_ui.py 来进行无界面预测。
    • 使用 tools/check_img.py 和 tools/check_ann.py 检查图像和注释文件的有效性。
  • 注意事项:

    • 确保所有必要的工具箱已安装,特别是 PyTorch 和 PyQt5。
    • 根据需要调整参数,如 epochs 和 batch_size。
  • 示例

    假设你的数据文件夹结构如下:

    dataset/
    ├── images/
    │ ├── train/
    │ │ ├── image1.jpg
    │ │ ├── image2.jpg
    │ │ └── …
    │ ├── valid/
    │ │ ├── image3.jpg
    │ │ ├── image4.jpg
    │ │ └── …
    │ └── test/
    │ ├── image5.jpg
    │ ├── image6.jpg
    │ └── …
    ├── annotations/
    │ ├── train/
    │ │ ├── image1.xml
    │ │ ├── image2.xml
    │ │ └── …
    │ ├── valid/
    │ │ ├── image3.xml
    │ │ ├── image4.xml
    │ │ └── …
    │ └── test/
    │ ├── image5.xml
    │ ├── image6.xml
    │ └── …
    └── dataset.json

    并且每个 .xml 文件中都有正确的 Pascal VOC 格式的标注。运行 ui.py 后,你可以通过点击按钮来加载图像并进行光伏电池板检测。

    赞(0)
    未经允许不得转载:171主机测评 » 光伏电池板语义分割系统 光伏电池板语义分割数据集
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址