

🧚
문제는 간단하나 생각보다 시간이 오래걸렸다.
오래 걸린 이유 또한 간단했다.
단계적 풀이를 하지 않아서였다.
이렇게 방향키(?)같은 문제를 접근할 때에는 한 개의 방향키 당 접근해야 한다는 것을 깨달았다.
또한 여러 에러들을 마주했는데
return값이 없을 경우
- 모든 조건에 return값을 명시해주었는지 확인해야 한다.
TypeError: object of type 'NoneType' has no len()
IndexError 가 날 경우
- len() 사용시에 행/열을 올바르게 선택했는지 찾으려는 대상이 len을 올바르게 갖고 있는지를 확인해야 한다.
또한 해당 문제에서는 행접근, 열접근을 유의해서 진행했어야 했다.
해당 문제에서는 열 접근할 때에는 문자열 형태로 되어있기 때문에 행 접근보다는 easy하게 그저 해당하는 행을 찾아 열 값들을 변경했는데
행 접근 시에는 모든 행/열을 방문해 결과적으로는 모든 값들을 변경했어야 했다.
열 접근 시에
new = ""
for i in range(len(park[now[0]])):
if i == y:
new += "S"
elif i == now[1]:
new += "O"
else:
new += park[now[0]][i]
park[now[0]] = new
행 접근 시에
new_arr = []
for i in range(len(park)):
new = ""
for j in range(len(park[i])):
if i == x and j == now[1]:
new += "S"
elif i == now[0] and j == now[1]:
new += "O"
else:
new += park[i][j]
new_arr.append(new)
park = new_arr
📌
def position(park):
for i in range(len(park)):
for j in range(len(park[i])):
if park[i][j] == "S":
return (i, j)
def moving(direction, move, park):
now = position(park)
int_move = int(move)
if direction == "E":
y = now[1] + int_move
if y >= len(park[now[0]]):
return park
for i in range(now[1], y+1):
if park[now[0]][i] == "X":
return park
new = ""
for i in range(len(park[now[0]])):
if i == y:
new += "S"
elif i == now[1]:
new += "O"
else:
new += park[now[0]][i]
park[now[0]] = new
return park
elif direction == "S":
x = now[0] + int_move
if x >= len(park):
return park
for i in range(now[0], x+1):
if park[i][now[1]] == "X":
return park
new_arr = []
for i in range(len(park)):
new = ""
for j in range(len(park[i])):
if i == x and j == now[1]:
new += "S"
elif i == now[0] and j == now[1]:
new += "O"
else:
new += park[i][j]
new_arr.append(new)
park = new_arr
return park
elif direction == "N":
x = now[0] - int_move
if x < 0:
return park
for i in range(x, now[0]+1):
if park[i][now[1]] == "X":
return park
new_arr = []
for i in range(len(park)):
new = ""
for j in range(len(park[i])):
if i == x and j == now[1]:
new += "S"
elif i == now[0] and j == now[1]:
new += "O"
else:
new += park[i][j]
new_arr.append(new)
park = new_arr
return park
elif direction == "W":
y = now[1] - int_move
if y < 0:
return park
for i in range(y, now[1]+1):
if park[now[0]][i] == "X":
return park
new = ""
for i in range(len(park[now[0]])):
if i == y:
new += "S"
elif i == now[1]:
new += "O"
else:
new += park[now[0]][i]
park[now[0]] = new
return park
def solution(park, routes):
answer = []
for route in routes:
direction = route.split()[0]
move = route.split()[1]
park = moving(direction, move, park)
answer.append(position(park)[0])
answer.append(position(park)[1])
return answer
YOLO는 객체 탐지 모델 중 하나로, 이미지에서 객체의 위치와 종류를 동시에 예측하는 강력한 딥러닝 모델임.
YOLO는 한 번의 신경망 전파만으로 객체를 탐지하기 때문에 실시간 처리가 가능할 정도로 매우 빠름
- YOLO는 이미지가 들어오면 그리드로 분할함
- 바운딩 박스 : 탐지된 객체를 둘러싸는 직사각형
- confidence score : 바운딩 박스가 객체를 포함할 가능성
객체가 바운딩 박스 내에 존재할 확률 (Objectness Score)
— 객체가 해당 바운딩 박스 내에 있을 확률을 나타냅니다.
객체의 종류에 대한 확률 (Class Score)
— 바운딩 박스 내에 객체가 존재할 경우, 그 객체가 특정 클래스에 속할 확률을 나타냅니다.
둘을 곱한 값이 confidence score임- 이미지 전체에서 객체가 특정 클래스에 속할 가능성을 예측하는 것이 목적
- 만약 여러 개의 바운딩 박스가 하나의 객체를 탐지한 경우? 중복된 바운딩 박스를 제거하고 가장 신뢰도가 높은 박스만 남기는 과정이 포함됨, 즉 중복된 예측을 줄임
YOLO는 CNN기반으로 여러 개의 합성곱층과 풀링층을 거쳐 이미지의 특징을 추출하고 바운딩 박스와 클래스 확률을 예측하는 레이어로 통과하게 됨
기본 코드
from ultralytics import YOLO
import cv2
from matplotlib import pyplot as plt
# 웹캠을 통해 실시간 객체 탐지
cap = cv2.VideoCapture(0) # 웹캠 캡처 시작
# 웹캠 ->
while True:
ret, frame = cap.read()
if not ret:
break
# 프레임을 YOLOv8 모델에 입력하여 객체 탐지 수행
results = model(frame)
# 이후 처리코드
# 'q' 키를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 웹캠 종료 및 창 닫기
cap.release()
cv2.destroyAllWindows()
응용 코드
PyQt5 : GUI를 생성하고 웹캠을 시작, 중지할 수 있는 버튼을 추가
FLOW 설명 :
앞서 conda를 이용해 설치한 PyQt5의 GUI 앱을 실행하고 VideoCaptureWidget객체를 띄움
VideoCaptureWidget 객체 클래스 생성
해당 클래스에서는 모델을 로드 한 후 yolov8 모델 파일로 초기화 함
UI를 띄움
웹캠 설정 : 웹캠 초기화와 timer 설정이 들어감
웹캠 start버튼 누를 시에 : 웹캠 장치가 열리며 timer를 20ms로 업데이트 함
웹캠 stop 버튼 누를 시에 : 웹캠 중지 및 타이머 멈춤
웹캠 연결과 함께 (초기화와 함께) : 객체 탐지 결과를 UI에 표시함
YOLOv8이 객체 탐지를 수행하고
바운딩 박스가 포함된 이미지를 가져옴 : results[0].plot()
가져온 이미지를 QImage로 변환함
QImage를 QLabel에 표시하기 위해 QPixmap으로 변환
from ultralytics import YOLO
import cv2
from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget, QPushButton
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QImage, QPixmap
class VideoCaptureWidget(QWidget):
def __init__(self):
super().__init__()
# YOLOv8x 모델 로드 (YOLOv8x)
self.model = YOLO('yolov8x.pt')
# UI 설정
self.setWindowTitle("실시간 객체 탐지")
self.image_label = QLabel(self)
self.layout = QVBoxLayout()
self.layout.addWidget(self.image_label)
self.start_button = QPushButton("Start Webcam", self)
self.start_button.clicked.connect(self.start_webcam)
self.layout.addWidget(self.start_button)
self.stop_button = QPushButton("Stop Webcam", self)
self.stop_button.clicked.connect(self.stop_webcam)
self.layout.addWidget(self.stop_button)
self.setLayout(self.layout)
# 웹캠 초기화
self.capture = None
# QTimer를 통해 주기적으로 새로운 프레임을 읽고 업데이트 할 수 있음
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_frame)
def start_webcam(self):
"""웹캠을 시작하고, 타이머를 시작하여 프레임을 주기적으로 읽음"""
self.capture = cv2.VideoCapture(0) # 웹캠 장치 열기
self.timer.start(20) # 20ms마다 프레임 업데이트 (50fps)
def stop_webcam(self):
"""웹캠을 중지하고 타이머를 멈춤"""
self.timer.stop()
if self.capture is not None:
self.capture.release()
def update_frame(self):
"""웹캠에서 프레임을 읽어와서 YOLO 객체 탐지를 수행한 후 UI에 표시"""
ret, frame = self.capture.read()
if ret:
# YOLOv8 객체 탐지 수행
results = self.model(frame)
result = results[0]
# 바운딩 박스가 포함된 이미지를 가져옴
img_with_boxes = result.plot()
# OpenCV 이미지를 QImage로 변환
rgb_image = cv2.cvtColor(img_with_boxes, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
convert_to_Qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
# QImage를 QLabel에 표시하기 위해 QPixmap으로 변환
self.image_label.setPixmap(QPixmap.fromImage(convert_to_Qt_format))
def closeEvent(self, event):
"""윈도우 닫을 때 웹캠 해제"""
if self.capture is not None:
self.capture.release()
if __name__ == "__main__":
app = QApplication([])
window = VideoCaptureWidget()
window.show()
app.exec_()
YOLO 응용
처음 접근 : 사람인식 -> 인종, 나이 맞춰보기
수정한 접근 : 데이터셋에 노이즈가 많이 낌을 포착 -> 화질 개선 모델 생성 우선 접근으로 수정
괜찮은 resize 찾기 위해 subplot 생성해 비교
path = SAMPLE_PATH
label = os.path.basename(os.path.dirname(path))
# 이미지 읽기
img = cv2.imread(path)
# 비율 계산
height, width = img.shape[:2]
aspect_ratio = width / height
# 여러 크기 정의
new_sizes = [(100, 100), (200, 200), (300, 300), (400, 400)]
# 서브플롯 크기 설정
fig, axes = plt.subplots(1, len(new_sizes), figsize=(15, 5))
for idx, new_size in enumerate(new_sizes):
# 리사이즈 계산
if aspect_ratio > 1:
new_width = new_size[0]
new_height = int(new_width / aspect_ratio)
else:
new_height = new_size[1]
new_width = int(new_height * aspect_ratio)
# 리사이즈
resized_img = cv2.resize(img, (new_width, new_height))
# 결과 출력 (RGB로 변환 후 표시)
axes[idx].imshow(cv2.cvtColor(resized_img, cv2.COLOR_BGR2RGB))
axes[idx].set_title(f"Size: {new_size}")
axes[idx].axis('off') # 축 숨기기
# 화면에 출력
plt.tight_layout()
plt.show()
학습 데이터와 검증 데이터 분할
resized_path = RESIZE_DIR_PATH
images = []
labels = []
def preProcessing(file_path, images, lables):
label = os.path.basename(os.path.dirname(file_path))
img = cv2.imread(file_path)
# 리사이즈
resized_img = cv2.resize(img, (400, 400))
images.append(img)
labels.append(label)
# 리사이즈된 이미지를 저장할 경로 설정
# resized_path에 바로 저장
# filename = os.path.basename(file_path)
# resized_filename = f"resized_{filename}"
# save_file_path = os.path.join(resized_path, resized_filename)
# # 리사이즈된 이미지를 저장
# cv2.imwrite(save_file_path, resized_img)
error handling - dataset pickle 화 실패
- 이미지 데이터이고 데이터의 수가 컸기 때문에 pickle화를 실패했다.