YOLOv8을 사용한 태양광 패널 결함 검출 시스템 구축: 12가지 결함 유형 포함

디ープ러닝 기반 객체 검출 프레임워크인 YOLOv8을 활용해 태양광 패널 결함 데이터셋을 학습하여 결함 탐지 시스템을 구축하는 방법입니다. 데이터셋에는 오염, 물체 가림, 새 날개 자국, 빛 반사, 구조 변형, 패널 파손, 눈 덮음 등 총 12가지 결함 유형이 포함되어 있습니다.

데이터셋은 시각적 정보를 포함하며, 총 55,200장의 이미지로 구성되어 있습니다. XML과 TXT 형식의 라벨 파일이 함께 제공되며, YOLO 트레이닝에 바로 사용 가능합니다. 아래는 구체적인 진행 단계입니다:

환경 설정

필요한 라이브러리를 설치하세요:

pip install ultralytics opencv-python-headless pandas scikit-learn

데이터셋 구조화

다음과 같은 디렉토리 구조를 갖춘 데이터셋을 준비해야 합니다:

datasets/solar_defects/
├── images/
│   ├── train/
│   ├── val/
│   └── test/
└── labels/
    ├── train/
    ├── val/
    └── test/

각 이미지에 대응하는 TXT 라벨 파일이 동일한 이름으로 존재해야 합니다.

YAML 구성 파일 생성

YOLOv8용 YAML 파일을 생성하세요:

[]
train: ../datasets/solar_defects/images/train
val: ../datasets/solar_defects/images/val
test: ../datasets/solar_defects/images/test

nc: 12
names: ['오염', '물체 가림', '새 날개 자국', '빛 반사', '구조 변형', '패널 파손', '눈 덮음', '기타1', '기타2', '기타3', '기타4', '기타5']

데이터셋 분할 스크립트

8:1:1 비율로 데이터셋을 분할하는 스크립트:

[]
import os
from sklearn.model_selection import train_test_split

# 기본 경로 설정
base_path = 'datasets/solar_defects'
images_dir = os.path.join(base_path, 'images')
labels_dir = os.path.join(base_path, 'labels')

# 분할 비율
train_ratio = 0.8
val_ratio = 0.1

# 이미지 목록 불러오기
image_files = [f for f in os.listdir(images_dir) if f.endswith('.jpg')]

# 랜덤 섞기
random.shuffle(image_files)

# 분할 인덱스 계산
train_split = int(len(image_files) * train_ratio)
val_split = int(len(image_files) * (train_ratio + val_ratio))

# 분할 수행
train_images = image_files[:train_split]
val_images = image_files[train_split:val_split]
test_images = image_files[val_split:]

# 파일 복사 로직
def copy_files(src_list, dst_img_dir, dst_lbl_dir):
    for img_name in src_list:
        img_path = os.path.join(images_dir, img_name)
        lbl_path = os.path.join(labels_dir, img_name.replace('.jpg', '.txt'))
        if os.path.exists(lbl_path):
            os.symlink(img_path, os.path.join(dst_img_dir, img_name))
            os.symlink(lbl_path, os.path.join(dst_lbl_dir, img_name.replace('.jpg', '.txt')))

copy_files(train_images, os.path.join(base_path, 'images/train'), os.path.join(base_path, 'labels/train'))
copy_files(val_images, os.path.join(base_path, 'images/val'), os.path.join(base_path, 'labels/val'))
copy_files(test_images, os.path.join(base_path, 'images/test'), os.path.join(base_path, 'labels/test'))

모델 훈련 스크립트

YOLOv8을 사용한 훈련:

[]
from ultralytics import YOLO

# 모델 로드
model = YOLO('yolov8n.pt')

# 훈련 실행
results = model.train(
    data='datasets/solar_defects.yaml',
    epochs=50,
    imgsz=640,
    batch=16,
    project='runs/train',
    name='solar_defect_detection'
)

# 모델 평가 및 변환
metrics = model.val()
results = model.export(format='onnx')

평가 및 시각화

검출 성능 평가:

[]
from ultralytics import YOLO

best_model = YOLO('runs/train/solar_defect_detection/weights/best.pt')
metrics = best_model.val(data='datasets/solar_defects.yaml', conf=0.5, iou=0.45)
print(metrics)

GUI 인터페이스

PyQt5를 사용한 사용자 인터페이스:

[]
import sys
from PyQt5.QtWidgets import *
from ultralytics import YOLO
import cv2

# 모델 로드
detection_model = YOLO('runs/train/solar_defect_detection/weights/best.pt')

class App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("태양광 패널 결함 검출 시스템")
        self.setGeometry(100, 100, 800, 600)

        self.initUI()

    def initUI(self):
        self.image_label = QLabel(self)
        self.image_label.setAlignment(Qt.AlignCenter)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.image_label)
        self.setLayout(self.layout)

        self.load_button = QPushButton("이미지 로드", self)
        self.load_button.clicked.connect(self.load_image)
        self.layout.addWidget(self.load_button)

        self.predict_button = QPushButton("검출 시작", self)
        self.predict_button.clicked.connect(self.start_prediction)
        self.layout.addWidget(self.predict_button)

        self.stop_button = QPushButton("중단", self)
        self.stop_button.clicked.connect(self.stop_prediction)
        self.layout.addWidget(self.stop_button)

    def load_image(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "이미지 선택", "", "Images (*.png *.jpg)")
        if file_name:
            self.display_image(file_name)

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

    def start_prediction(self):
        if self.image_path:
            self.timer = QTimer()
            self.timer.timeout.connect(self.update_frame)
            self.timer.start(30)

    def stop_prediction(self):
        if hasattr(self, 'timer'):
            self.timer.stop()
            self.image_label.clear()

    def update_frame(self):
        img = cv2.imread(self.image_path)
        results = detection_model.predict(img, size=640, conf=0.5, iou=0.45)[0]
        
        for box in results.boxes.cpu().numpy():
            r = box.xyxy[0].astype(int)
            cls = int(box.cls[0])
            conf = box.conf[0]
            class_names = ['오염', '물체 가림', '새 날개 자국', '빛 반사', '구조 변형', '패널 파손', '눈 덮음', '기타1', '기타2', '기타3', '기타4', '기타5']
            class_name = class_names[cls]
            
            cv2.rectangle(img, (r[0], r[1]), (r[2], r[3]), (0, 255, 0), 2)
            cv2.putText(img, f'{class_name} ({conf:.2f})', (r[0], r[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2)
        
        h, w = img.shape[:2]
        bytes_per_line = w * 3
        qt_img = QImage(img.data, w, h, bytes_per_line, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(qt_img).scaled(self.image_label.width(), self.image_label.height())
        self.image_label.setPixmap(pixmap)

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

실행 전 다음과 같은 조건을 확인하세요:

  • 데이터셋 구조가 정확하게 구성되었는지 확인
  • 모델 가중치 파일 경로가 올바르게 설정되어 있는지 확인
  • 필수 라이브러리 설치 여부 확인
  • 훈련 파라미터 조정 필요 시 수정

태그: YOLOv8 태양광 패널 결함 객체 검출 PyQt5 딥러닝

7월 16일 19:35에 게시됨