import cv2
import numpy as np
from collections import deque
import serial
import serial.tools.list_ports
import tkinter as tk
from tkinter import ttk, messagebox
import threading
import time
# 시리얼 포트 설정
ser = None
moving_avg_frame = None # 전역 변수로 선언
moving_windows_size = 30 # 웹캠의 FPS 30기준 90설정으로 3초 무빙 적용
def connect_serial(port):
global ser
try:
ser = serial.Serial(port, 9600, timeout=1)
messagebox.showinfo("Info", f"Connected to {port}")
except Exception as e:
messagebox.showerror("Error", str(e))
def disconnect_serial():
global ser
if ser and ser.is_open:
ser.close()
messagebox.showinfo("Info", "Disconnected")
def send_serial_data(data):
global ser
if ser and ser.is_open:
print(data)
ser.write(data.encode())
def get_serial_ports():
ports = serial.tools.list_ports.comports()
return [port.device for port in ports]
def on_select_port(event):
global selected_port
selected_port = event.widget.get()
def create_gui(window_width=400, window_height=300):
global selected_port
selected_port = None
root = tk.Tk()
root.title("Serial Port Selector")
root.geometry(f"{window_width}x{window_height}")
tk.Label(root, text="Select Serial Port:").pack(pady=10)
ports = get_serial_ports()
if not ports:
messagebox.showerror("Error", "No serial ports found")
return
port_var = tk.StringVar(value=ports)
port_menu = ttk.Combobox(root, textvariable=port_var, values=ports)
port_menu.pack(pady=10)
port_menu.bind("<<ComboboxSelected>>", on_select_port)
connect_button = tk.Button(root, text="Connect", command=lambda: connect_serial(selected_port))
connect_button.pack(pady=10)
disconnect_button = tk.Button(root, text="Disconnect", command=disconnect_serial)
disconnect_button.pack(pady=10)
root.mainloop()
def send_frame_data(data):
global moving_avg_frame, ser
if moving_avg_frame is not None and ser and ser.is_open:
frame_data = "[" + ",".join(map(str, data)) + "]"
send_serial_data(frame_data)
def main():
global moving_avg_frame, list_moving_avg_color
list_moving_avg_color = list()
cap = cv2.VideoCapture(0) # 0번 카메라 사용
if not cap.isOpened():
print("웹캠을 열 수 없습니다.")
return
# 각 구역의 밝기 값을 저장할 데이터 구조
num_rows, num_cols = 3, 4
block_brightness_history = [[deque(maxlen=moving_windows_size) for _ in range(num_cols)] for _ in range(num_rows)]
# 시리얼 데이터를 송신하는 스레드 시작
#threading.Thread(target=send_frame_data, daemon=True).start()
start_time = 0
end_time = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 프레임을 YUV 색상 모델로 변환
yuv = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV)
# Y 채널 (밝기)만 추출
y_channel = yuv[:, :, 0]
# 프레임 크기
height, width = y_channel.shape
# 각 구역의 크기
block_height = height // num_rows
block_width = width // num_cols
# 새로운 빈 이미지 생성
output_frame = np.zeros((height, width, 3), dtype=np.uint8)
moving_avg_frame = np.zeros((height, width, 3), dtype=np.uint8)
secotrCnt = 0
for i in range(num_rows):
for j in range(num_cols):
# 각 구역에 대한 좌표 계산
y_start = i * block_height
y_end = (i + 1) * block_height
x_start = j * block_width
x_end = (j + 1) * block_width
# 해당 구역의 밝기 정보 추출
block = y_channel[y_start:y_end, x_start:x_end]
# 구역의 평균 밝기 값 계산
avg_brightness = np.mean(block)
# 밝기 값을 기록
block_brightness_history[i][j].append(avg_brightness)
# 무빙 에버리지 계산
moving_avg_brightness = np.mean(block_brightness_history[i][j])
# 평균 밝기 값을 시각화 색상으로 매핑
color = map_brightness_to_color(avg_brightness)
moving_avg_color = map_brightness_to_color(moving_avg_brightness)
# 구역에 평균 밝기 값을 시각화하여 출력 프레임에 적용
output_frame[y_start:y_end, x_start:x_end] = color
moving_avg_frame[y_start:y_end, x_start:x_end] = moving_avg_color
# RS232로 전송할 섹션에 계산된 moving_avg_color을 리스트에 저장 로직
if len(list_moving_avg_color) < 12:
list_moving_avg_color.append(int(round(moving_avg_brightness)))
else:
list_moving_avg_color[secotrCnt] = int(round(moving_avg_brightness))
# 구역의 번호 표시
cv2.putText(output_frame, f"Sector {int(secotrCnt)}", (x_start + 10, y_start + 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.putText(moving_avg_frame, f"Sector {int(secotrCnt)}", (x_start + 10, y_start + 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
secotrCnt += 1
# 구역의 평균 밝기 값을 화면에 숫자로 표시
cv2.putText(output_frame, f"{int(avg_brightness)}", (x_start + 10, y_end - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
cv2.putText(moving_avg_frame, f"{int(moving_avg_brightness)}", (x_start + 10, y_end - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
# 원본 이미지
cv2.imshow('(1)Original Model', frame)
# RGB to YUV 영상
cv2.imshow('(2)Color Model(RGB to YUV)', yuv)
# 밝기 정보를 화면에 표시
cv2.imshow('(3)Y Channel (Brightness)', output_frame)
# 무빙 에버리지 필터 적용 결과를 새로운 창에 표시
cv2.imshow('(4)Moving Average Brightness', moving_avg_frame)
# 'q' 키를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
end_time = time.time() * 1000
if end_time-start_time > 100:
if(len(list_moving_avg_color) == 12):
send_frame_data(list_moving_avg_color)
start_time = time.time()*1000
cap.release()
cv2.destroyAllWindows()
def map_brightness_to_color(brightness):
# 밝기 값을 시각화 색상으로 매핑
color_value = int(brightness)
return np.array([color_value, color_value, color_value], dtype=np.uint8)
if __name__ == "__main__":
# GUI 창의 크기를 설정하는 변수
window_width = 500 # 원하는 가로 크기로 수정
window_height = 400 # 원하는 세로 크기로 수정
threading.Thread(target=lambda: create_gui(window_width, window_height), daemon=True).start()
main()