diff --git a/requirements.txt b/requirements.txt index 50f7a13..57f184f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ PyQt5 >= 5.13 python-vlc PyQtGraph -pyechart +PyQtChart influxdb opencv-python -pandas \ No newline at end of file +pandas +psutil +scipy \ No newline at end of file diff --git a/run.py b/run.py index b1a3bda..587b069 100644 --- a/run.py +++ b/run.py @@ -6,6 +6,31 @@ # cotjdals5450@gmail.com (Seong Min Chae) # 5jx2oh@gmail.com (Jongjin Oh) -from src.__main__ import main +import multiprocessing as mp +from src.nd01 import clock, ND01MainWindow -main() + +if __name__ == '__main__': + import sys + from PyQt5.QtWidgets import QApplication + print(f'Start time: {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}') + + mp.freeze_support() + q = Queue() + _q = Queue() + + _producer = producer + + p = Process(name='clock', target=clock, args=(q,), daemon=True) + _p = Process(name='producer', target=_producer, args=(_q,), daemon=True) + + p.start() + _p.start() + + os.makedirs(f'{JS06Settings.get("data_csv_path")}', exist_ok=True) + os.makedirs(f'{JS06Settings.get("target_csv_path")}', exist_ok=True) + os.makedirs(f'{JS06Settings.get("image_save_path")}', exist_ok=True) + + app = QApplication(sys.argv) + window = ND01MainWindow(q, _q) + sys.exit(app.exec()) diff --git a/src/auto_file_delete.py b/src/auto_file_delete.py new file mode 100644 index 0000000..ae11839 --- /dev/null +++ b/src/auto_file_delete.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 9th grade 5th class. +# +# Authors: +# 5jx2oh@gmail.com + +import os +import psutil +import shutil + +from PyQt5.QtWidgets import (QDialog, QApplication, QMessageBox) +from PyQt5 import uic + +from model import JS08Settings + + +def byte_transform(bytes, to, bsize=1024): + """ + Unit conversion of byte received from shutil + + :return: Capacity of the selected unit (int) + """ + unit = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4} + r = float(bytes) + for i in range(unit[to]): + r = r / bsize + return int(r) + + +class FileAutoDelete(QDialog): + + def __init__(self): + super().__init__() + + ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'resources/auto_file_delete.ui') + uic.loadUi(ui_path, self) + + # self.setFixedSize(self.width(), self.height()) + + drive = [] + # Save all of the user's drives in drive variable. + for i in range(len(psutil.disk_partitions())): + drive.append(str(psutil.disk_partitions()[i])[18:19]) + + self.calendarWidget.activated.connect(self.showDate) + + self.path = None + self.date = None + self.date_convert = None + + self.exit_pushButton.clicked.connect(self.exit_click) + + def exit_click(self): + self.close() + + def showDate(self, date): + self.date = date.toString('yyMMdd') + self.date_convert = date.toString('yyyy/MM/dd') + self.check_file_date(os.path.join(JS08Settings.get('image_save_path'), + 'vista')) + + def check_file_date(self, path: str): + is_old = [] + + for f in os.listdir(path): + print(f) + if int(f) <= int(self.date): + is_old.append(int(f)) + + if is_old: + dlg = QMessageBox.question(self, 'Warning', f'Delete folder before {self.date_convert} ?', + QMessageBox.Yes | QMessageBox.No) + if dlg == QMessageBox.Yes: + self.delete_select_date(path, is_old) + else: + QMessageBox.information(self, 'Information', 'There is no data before the selected date.') + + def delete_select_date(self, path: str, folder: list): + """ + Delete the list containing the folder name + + :param path: Path to proceed with a auto-delete + :param folder: Data older than the date selected as the calendarWidget + """ + + for i in range(len(folder)): + a = os.path.join(path, str(folder[i])) + shutil.rmtree(a) + print(f'{a} delete complete.') + + +if __name__ == "__main__": + import sys + + app = QApplication(sys.argv) + window = FileAutoDelete() + window.show() + sys.exit(app.exec_()) diff --git a/src/cal_ext_coef.py b/src/cal_ext_coef.py new file mode 100644 index 0000000..02e8d3e --- /dev/null +++ b/src/cal_ext_coef.py @@ -0,0 +1,132 @@ +import itertools +import os + +import numpy as np +import pandas as pd +import traceback +import warnings + +import scipy +from scipy.optimize import curve_fit + +curved_flag = True +hanhwa_dist = [] +hanhwa_x = [] +hanhwa_r = [] +hanhwa_g = [] +hanhwa_b = [] + + +def select_max_rgb(r, g, b): + c_list = [r, g, b] + + c_index = c_list.index(max(c_list)) + + if c_index == 0: + select_color = 'red' + elif c_index == 1: + select_color = 'green' + else: + select_color = 'blue' + # select_color = 'green' + + return select_color + + +def cal_curve(hanhwa: pd.DataFrame): + # hanhwa = pd.read_csv(f"{rgbsavedir}/{epoch}.csv") + # print(hanhwa) + global hanhwa_opt_r, hanhwa_opt_g, hanhwa_opt_b + hanhwa = hanhwa.sort_values(by=['distance']) + hanhwa_dist = hanhwa[['distance']].squeeze().to_numpy() + hanhwa_x = np.linspace(hanhwa_dist[0], hanhwa_dist[-1], 100, endpoint=True) + hanhwa_x.sort() + hanhwa_r = hanhwa[['r']].squeeze().to_numpy() + hanhwa_g = hanhwa[['g']].squeeze().to_numpy() + hanhwa_b = hanhwa[['b']].squeeze().to_numpy() + + r1_init = hanhwa_r[0] * 0.7 + g1_init = hanhwa_g[0] * 0.7 + + b1_init = hanhwa_b[0] * 0.7 + r2_init = hanhwa_r[-1] * 1.3 + g2_init = hanhwa_g[-1] * 1.3 + b2_init = hanhwa_b[-1] * 1.3 + + select_color = select_max_rgb(r2_init, g2_init, b2_init) + + r_ext_init = [r1_init, r2_init, 1] + g_ext_init = [g1_init, g2_init, 1] + b_ext_init = [b1_init, b2_init, 1] + + try: + hanhwa_opt_r, hanhwa_cov_r = curve_fit(func, hanhwa_dist, hanhwa_r, p0=r_ext_init, maxfev=5000) + hanhwa_opt_g, hanhwa_cov_g = curve_fit(func, hanhwa_dist, hanhwa_g, p0=g_ext_init, maxfev=5000) + hanhwa_opt_b, hanhwa_cov_b = curve_fit(func, hanhwa_dist, hanhwa_b, p0=b_ext_init, maxfev=5000) + + # hanhwa_opt_r, hanhwa_cov_r = curve_fit(func, hanhwa_dist, hanhwa_r, p0=r_ext_init) + # hanhwa_opt_g, hanhwa_cov_g = curve_fit(func, hanhwa_dist, hanhwa_g, p0=g_ext_init) + # hanhwa_opt_b, hanhwa_cov_b = curve_fit(func, hanhwa_dist, hanhwa_b, p0=b_ext_init) + + except RuntimeError: + # Optimal parameters not found: Number of calls to function has reached maxfev = 5000. + # 이 에러는 타겟의 RGB에 대해 최적의 기울기를 찾지 못하여 발생하는 에러로, + # 타겟이 이상할 때 대게 발생함. + + # print(traceback.format_exc()) + pass + + list1 = [] + list2 = [] + list3 = [] + + list1.append(hanhwa_opt_r[0]) + list1.append(hanhwa_opt_g[0]) + list1.append(hanhwa_opt_b[0]) + + list2.append(hanhwa_opt_r[1]) + list2.append(hanhwa_opt_g[1]) + list2.append(hanhwa_opt_b[1]) + + list3.append(hanhwa_opt_r[2]) + list3.append(hanhwa_opt_g[2]) + list3.append(hanhwa_opt_b[2]) + + # hanhwa_err_r = np.sqrt(np.diag(hanhwa_cov_r)) + # hanhwa_err_g = np.sqrt(np.diag(hanhwa_cov_g)) + # hanhwa_err_b = np.sqrt(np.diag(hanhwa_cov_b)) + + # print_result(hanhwa_opt_r, hanhwa_opt_g, hanhwa_opt_b, hanhwa_err_r, hanhwa_err_g, hanhwa_err_b) + + # print(f"Red channel: {extcoeff_to_vis(hanhwa_opt_r[2], hanhwa_err_r[2], 3)} km") + # print(f"Green channel: {extcoeff_to_vis(hanhwa_opt_g[2], hanhwa_err_g[2], 3)} km") + # print(f"Blue channel: {extcoeff_to_vis(hanhwa_opt_b[2], hanhwa_err_b[2], 3)} km") + + # os.makedirs(extsavedir, exist_ok=True) + + return list1, list2, list3, select_color + + +# @staticmethod +def func(x, c1, c2, a): + warnings.filterwarnings('ignore') + return c2 + (c1 - c2) * np.exp(-a * x) + + +# def print_result(opt_r, opt_g, opt_b, err_r, err_g, err_b): +# print(f'Red channel: (', +# f'C1: {opt_r[0]:.2f} ± {err_r[0]:.2f}, ', +# f'C2: {opt_r[1]:.2f} ± {err_r[1]:.2f}, ', +# f'alpha: {opt_r[2]:.2f} ± {err_r[2]:.2f})') +# print(f'Green channel: (', +# f'C1: {opt_g[0]:.2f} ± {err_g[0]:.2f}, ', +# f'C2: {opt_g[1]:.2f} ± {err_g[1]:.2f}, ', +# f'alpha: {opt_g[2]:.2f} ± {err_g[2]:.2f})') +# print(f'Blue channel: (', +# f'C1: {opt_b[0]:.2f} ± {err_b[0]:.2f}, ', +# f'C2: {opt_b[1]:.2f} ± {err_b[1]:.2f}, ', +# f'alpha: {opt_b[2]:.2f} ± {err_b[2]:.2f})') + + +# def extcoeff_to_vis(optimal, error, coeff=3.912): +# return coeff / (optimal + np.array((1, 0, -1)) * error) diff --git a/src/calculation/curve_save.py b/src/calculation/curve_save.py deleted file mode 100644 index 62bf6dd..0000000 --- a/src/calculation/curve_save.py +++ /dev/null @@ -1,124 +0,0 @@ -import itertools -import os - -import numpy as np -import pandas as pd - -import scipy -from scipy.optimize import curve_fit -from PyQt5 import QtWidgets, QtGui, QtCore - -curved_flag = True -# cam_name = cam_name -hanhwa_dist = [] -hanhwa_x = [] -hanhwa_r = [] -hanhwa_g = [] -hanhwa_b = [] -# epoch = epoch -# rgbsavedir = os.path.join(f"rgb/{cam_name}") -# extsavedir = os.path.join(f"extinction/{cam_name}") - -def select_max_rgb(r, g, b): - - select_color = "" - c_list = [r, g, b] - - c_index = c_list.index(max(c_list)) - - if c_index == 0: - select_color = "red" - elif c_index == 1: - select_color = "green" - else : - select_color = "blue" - return select_color - -def cal_curve(hanhwa: pd.DataFrame): - # hanhwa = pd.read_csv(f"{rgbsavedir}/{epoch}.csv") - print(hanhwa) - hanhwa = hanhwa.sort_values(by=['distance']) - hanhwa_dist = hanhwa[['distance']].squeeze().to_numpy() - hanhwa_x = np.linspace(hanhwa_dist[0], hanhwa_dist[-1], 100, endpoint=True) - hanhwa_x.sort() - hanhwa_r = hanhwa[['r']].squeeze().to_numpy() - hanhwa_g = hanhwa[['g']].squeeze().to_numpy() - hanhwa_b = hanhwa[['b']].squeeze().to_numpy() - - r1_init = hanhwa_r[0] * 0.7 - g1_init = hanhwa_g[0] * 0.7 - b1_init = hanhwa_b[0] * 0.7 - - r2_init = hanhwa_r[-1] * 1.3 - g2_init = hanhwa_g[-1] * 1.3 - b2_init = hanhwa_b[-1] * 1.3 - - select_color = select_max_rgb(r2_init, g2_init, b2_init) - - r_ext_init = [r1_init, r2_init, 1] - g_ext_init = [g1_init, g2_init, 1] - b_ext_init = [b1_init, b2_init, 1] - - try: - - hanhwa_opt_r, hanhwa_cov_r = curve_fit(func, hanhwa_dist, hanhwa_r, p0=r_ext_init, maxfev=5000) - hanhwa_opt_g, hanhwa_cov_g = curve_fit(func, hanhwa_dist, hanhwa_g, p0=g_ext_init, maxfev=5000) - hanhwa_opt_b, hanhwa_cov_b = curve_fit(func, hanhwa_dist, hanhwa_b, p0=b_ext_init, maxfev=5000) - - except Exception as e: - print("error msg: ", e) - return - list1 = [] - list2 = [] - list3 = [] - - list1.append(hanhwa_opt_r[0]) - list1.append(hanhwa_opt_g[0]) - list1.append(hanhwa_opt_b[0]) - - list2.append(hanhwa_opt_r[1]) - list2.append(hanhwa_opt_g[1]) - list2.append(hanhwa_opt_b[1]) - - list3.append(hanhwa_opt_r[2]) - list3.append(hanhwa_opt_g[2]) - list3.append(hanhwa_opt_b[2]) - - hanhwa_err_r = np.sqrt(np.diag(hanhwa_cov_r)) - hanhwa_err_g = np.sqrt(np.diag(hanhwa_cov_g)) - hanhwa_err_b = np.sqrt(np.diag(hanhwa_cov_b)) - - print_result(hanhwa_opt_r, hanhwa_opt_g, hanhwa_opt_b, hanhwa_err_r, hanhwa_err_g, hanhwa_err_b) - - print(f"Red channel: {extcoeff_to_vis(hanhwa_opt_r[2], hanhwa_err_r[2], 3)} km") - print(f"Green channel: {extcoeff_to_vis(hanhwa_opt_g[2], hanhwa_err_g[2], 3)} km") - print(f"Blue channel: {extcoeff_to_vis(hanhwa_opt_b[2], hanhwa_err_b[2], 3)} km") - - return list1, list2, list3, select_color - # update_extinc_signal.emit(list1, list2, list3, select_color) - - try: - os.mkdir(extsavedir) - except Exception as e: - pass - -# @staticmethod -def func(x, c1, c2, a): - return c2 + (c1 - c2) * np.exp(-a * x) - -def print_result(opt_r, opt_g, opt_b, err_r, err_g, err_b): - print(f"Red channel: (", - f"C1: {opt_r[0]:.2f} ± {err_r[0]:.2f}, ", - f"C2: {opt_r[1]:.2f} ± {err_r[1]:.2f}, ", - f"alpha: {opt_r[2]:.2f} ± {err_r[2]:.2f})") - print(f"Green channel: (", - f"C1: {opt_g[0]:.2f} ± {err_g[0]:.2f}, ", - f"C2: {opt_g[1]:.2f} ± {err_g[1]:.2f}, ", - f"alpha: {opt_g[2]:.2f} ± {err_g[2]:.2f})") - print(f"Blue channel: (", - f"C1: {opt_b[0]:.2f} ± {err_b[0]:.2f}, ", - f"C2: {opt_b[1]:.2f} ± {err_b[1]:.2f}, ", - f"alpha: {opt_b[2]:.2f} ± {err_b[2]:.2f})") - -def extcoeff_to_vis(optimal, error, coeff=3.291): - return coeff / (optimal + np.array((1, 0, -1)) * error) diff --git a/src/calculation/gary_visibility.py b/src/calculation/gary_visibility.py deleted file mode 100644 index 113eebf..0000000 --- a/src/calculation/gary_visibility.py +++ /dev/null @@ -1,106 +0,0 @@ -import itertools -import os - -import numpy as np -import pandas as pd - -import scipy -from scipy.optimize import curve_fit -import matplotlib -import matplotlib.pyplot as plt - - -def minprint(self): - """지정한 구역들에서 소산계수 산출용 픽셀을 출력하는 함수""" - result = () - cnt = 1 - min_x = [] - min_y = [] - - for upper_left, lower_right in zip(left_range, right_range): - result = minrgb(upper_left, lower_right) - print(f"target{cnt}의 소산계수 검출용 픽셀위치 = ", result) - min_x.append(result[0]) - min_y.append(result[1]) - cnt += 1 - - get_rgb(epoch) - - curved_thread = CurvedThread(camera_name, epoch) - curved_thread.update_extinc_signal.connect(extinc_print) - curved_thread.run() - - list_test() - - graph_dir = os.path.join(f"extinction/{camera_name}") -def minrgb(self, upper_left, lower_right): - """드래그한 영역의 RGB 최솟값을 추출한다""" - - up_y = min(upper_left[1], lower_right[1]) - down_y = max(upper_left[1], lower_right[1]) - - left_x = min(upper_left[0], lower_right[0]) - right_x = max(upper_left[0], lower_right[0]) - - test = cp_image[up_y:down_y, left_x:right_x, :] - - # 드래그한 영역의 RGB 값을 각각 추출한다. - # r = test[:, :] - # g = test[:, :, 1] - # b = test[:, :, 2] - - # RGB값을 각 위치별로 모두 더한다. - # RGB 최댓값이 255로 정해져있어 값을 초과하면 0부터 시작된다. numpy의 clip 함수를 이용해 array의 최댓값을 수정한다. - # r = np.clip(r, 0, 765) - # sum_rgb = r + g + b - - # RGB 값을 합한 뒤 가장 최솟값의 index를 추출한다. - t_idx = np.where(test == np.min(test)) - - show_min_y = t_idx[0][0] + up_y - show_min_x = t_idx[1][0] + left_x - - return (show_min_x, show_min_y) - -def get_rgb(self, epoch: str): - r_list = [] - g_list = [] - b_list = [] - - for x, y in zip(min_x, min_y): - - r_list.append(cp_image[y, x, 0]) - g_list.append(cp_image[y, x, 1]) - b_list.append(cp_image[y, x, 2]) - - print("red : ", cp_image[y, x, 0]) - print("green : ", cp_image[y, x, 1]) - print("blue: ", cp_image[y, x, 2]) - - - save_rgb(r_list, g_list, b_list, epoch) - -def save_rgb(self, r_list, g_list, b_list, epoch): - """Save the rgb information for each target.""" - try: - save_path = os.path.join(f"rgb/{camera_name}") - os.mkdir(save_path) - - except Exception as e: - pass - - if r_list: - r_list = list(map(int, r_list)) - g_list = list(map(int, g_list)) - b_list = list(map(int, b_list)) - print(b_list) - - col = ["target_name", "r", "g", "b", "distance"] - result = pd.DataFrame(columns=col) - result["target_name"] = target_name - result["r"] = r_list - result["g"] = g_list - result["b"] = b_list - result["distance"] = distance - print(result.head(10)) - result.to_csv(f"{save_path}/{epoch}.csv", mode="w", index=False) \ No newline at end of file diff --git a/src/clock.py b/src/clock.py new file mode 100644 index 0000000..428d7ce --- /dev/null +++ b/src/clock.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + +import time + + +def clockclock(queue): + """Real-time clock + Current time to be expressed on JS-06 + + :param queue: MultiProcessing Queue + """ + while True: + now = str(time.time()) + queue.put(now) + time.sleep(1) diff --git a/src/controller.py b/src/controller.py index 2372a69..4268be7 100644 --- a/src/controller.py +++ b/src/controller.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # # Copyright 2020-2021 Sijung Co., Ltd. -# -# Authors: +# +# Authors: # ruddyscent@gmail.com (Kyungwon Chun) # 5jx2oh@gmail.com (Jongjin Oh) @@ -13,35 +13,27 @@ import cv2 import numpy as np -# import onnxruntime as ort from PyQt5.QtCore import (QDateTime, QDir, QObject, QRect, QThread, QThreadPool, QTime, QTimer, pyqtSignal, pyqtSlot) from PyQt5.QtGui import QImage -from PyQt5.QtMultimedia import QVideoFrame - -# from .model import (Js08AttrModel, Js08CameraTableModel, Js08IoRunner, -# Js08Settings, Js08SimpleTarget, Js08Wedge) -class MainCtrl(QObject): +class JS08MainCtrl(QObject): abnormal_shutdown = pyqtSignal() - front_camera_changed = pyqtSignal(str) # uri - rear_camera_changed = pyqtSignal(str) # uri + front_camera_changed = pyqtSignal(str) # uri + rear_camera_changed = pyqtSignal(str) # uri front_target_decomposed = pyqtSignal() rear_target_decomposed = pyqtSignal() - target_assorted = pyqtSignal(list, list) # positives, negatives - wedge_vis_ready = pyqtSignal(int, dict) # epoch, wedge visibility + target_assorted = pyqtSignal(list, list) # positives, negatives + wedge_vis_ready = pyqtSignal(int, dict) # epoch, wedge visibility - # def __init__(self, model: Js08AttrModel): def __init__(self): super().__init__() self.writer_pool = QThreadPool.globalInstance() self.writer_pool.setMaxThreadCount(1) - self._model = model - self.num_working_cam = 0 self.front_simple_targets = [] @@ -50,34 +42,11 @@ def __init__(self): self.front_target_prepared = False self.rear_target_prepared = False - self.init_db() - self.observation_timer = QTimer(self) - # self.front_camera_changed.connect(self.decompose_front_targets) - # self.rear_camera_changed.connect(self.decompose_rear_targets) self.worker_running = False self.start_observation_timer() - def init_db(self): - db_host = Js08Settings.get('db_host') - db_port = Js08Settings.get('db_port') - db_name = Js08Settings.get('db_name') - self._model.connect_to_db(db_host, db_port, db_name) - - if getattr(sys, 'frozen', False): - directory = sys._MEIPASS - else: - directory = os.path.dirname(__file__) - attr_path = os.path.join(directory, 'resources', 'attr.json') - with open(attr_path, 'r') as f: - attr_json = json.load(f) - camera_path = os.path.join(directory, 'resources', 'camera.json') - with open(camera_path, 'r') as f: - camera_json = json.load(f) - - self._model.setup_db(attr_json, camera_json) - @pyqtSlot(str) def decompose_front_targets(self, _: str) -> None: """Make list of SimpleTarget by decoposing compound targets. @@ -85,7 +54,6 @@ def decompose_front_targets(self, _: str) -> None: Parameters: """ self.front_target_prepared = False - # self.decompose_targets('front') self.front_target_prepared = True @pyqtSlot(str) @@ -112,9 +80,9 @@ def decompose_targets(self, direction: str) -> None: elif direction == 'rear': targets = attr['rear_camera']['targets'] id = str(attr['rear_camera']['camera_id']) - - base_path = Js08Settings.get('image_base_path') - + + base_path = Js08Settings.get('image_base_path') + # Prepare model. # TODO(Kyungwon): Put the model file into Qt Resource Collection. if getattr(sys, 'frozen', False): @@ -127,7 +95,7 @@ def decompose_targets(self, direction: str) -> None: input_shape = sess.get_inputs()[0].shape input_height = input_shape[1] input_width = input_shape[2] - + for tg in targets: wedge = tg['wedge'] azimuth = tg['azimuth'] @@ -172,7 +140,7 @@ def read_mask(self, path: str) -> np.ndarray: def start_observation_timer(self) -> None: print('DEBUG(start_observation_timer):', QTime.currentTime().toString()) - self.observation_timer.setInterval(1000) # every one second + self.observation_timer.setInterval(10000) # every 10 seconds self.observation_timer.timeout.connect(self.start_worker) self.observation_timer.start() @@ -193,11 +161,11 @@ def start_worker(self) -> None: rear_uri = self.get_rear_camera_uri() self.worker = Js08InferenceWorker( self.epoch, - front_uri, - rear_uri, - self.front_simple_targets, + front_uri, + rear_uri, + self.front_simple_targets, self.rear_simple_targets - ) + ) self.worker_thread = QThread() self.worker.moveToThread(self.worker_thread) self.worker_thread.started.connect(self.worker.run) @@ -216,7 +184,7 @@ def postproduction(self): epoch: seconds since epoch """ epoch = self.epoch - + pos, neg = self.assort_discernment() self.target_assorted.emit(pos, neg) wedge_vis = self.wedge_visibility() @@ -262,13 +230,13 @@ def wedge_visibility(self) -> dict: wedge_vis = {w: None for w in Js08Wedge} for t in self.front_simple_targets: if t.discernment: - if wedge_vis[t.wedge] == None: + if wedge_vis[t.wedge] is None: wedge_vis[t.wedge] = t.distance elif wedge_vis[t.wedge] < t.distance: wedge_vis[t.wedge] = t.distance for t in self.rear_simple_targets: if t.discernment: - if wedge_vis[t.wedge] == None: + if wedge_vis[t.wedge] is None: wedge_vis[t.wedge] = t.distance elif wedge_vis[t.wedge] < t.distance: wedge_vis[t.wedge] = t.distance @@ -286,7 +254,7 @@ def save_image(self, dir: str, filename: str, image: QImage) -> None: path = QDir.cleanPath(os.path.join(dir, filename)) runner = Js08IoRunner(path, image) self.writer_pool.start(runner) - + def grab_image(self, direction: str) -> QImage: """ Parameters: @@ -320,7 +288,7 @@ def get_target(self, direction: str) -> list: def get_camera_table_model(self) -> dict: cameras = self.get_cameras() - table_model = Js08CameraTableModel(cameras) + table_model = Js08CameraTableModel(cameras) return table_model def check_exit_status(self) -> bool: @@ -335,7 +303,7 @@ def update_cameras(self, cameras: list, update_target: bool = False) -> None: for cam_id in cam_id_in_db: if cam_id not in cam_id_in_arg: self._model.delete_camera(cam_id) - + # if `cameras` does not have 'targets' field, add an empty list for it. for cam in cameras: if 'targets' not in cam: @@ -349,7 +317,7 @@ def update_cameras(self, cameras: list, update_target: bool = False) -> None: if c_arg['_id'] == c_db['_id']: c_arg['targets'] = c_db['targets'] continue - + # if '_id' is empty, delete the field for cam in cameras: if not cam['_id']: @@ -369,7 +337,7 @@ def get_attr(self) -> dict: # if self._attr.count_documents({}): # attr_doc = list(self._attr.find().sort("_id", -1).limit(1))[0] return attr_doc - + def insert_attr(self, model: dict) -> None: self._model.insert_attr(model) @@ -379,7 +347,7 @@ def restore_defaults(self) -> None: @pyqtSlot(bool) def set_normal_shutdown(self) -> None: - Js08Settings.set('normal_shutdown', True) + Js08Settings.set('normal_shutdown', True) def get_cameras(self) -> list: return self._model.read_cameras() @@ -387,14 +355,15 @@ def get_cameras(self) -> list: class Js08InferenceWorker(QObject): finished = pyqtSignal() - - def __init__(self, epoch: int, front_uri: str, rear_uri: str, front_decomposed_targets: list, rear_decomposed_targets: list) -> None: + + def __init__(self, epoch: int, front_uri: str, rear_uri: str, front_decomposed_targets: list, + rear_decomposed_targets: list) -> None: """ Parameters: ctrl: """ super().__init__() - + # TODO(Kyungwon): Put the model file into Qt Resource Collection. if getattr(sys, 'frozen', False): directory = sys._MEIPASS @@ -470,7 +439,7 @@ def run(self): self.save_image(dir, filename, front_image) filename = f'vista-rear-{now.toString("yyyy-MM-dd-hh-mm")}.png' self.save_image(dir, filename, rear_image) - + # Discriminate the targets of front camera self.classify_batch(self.front_targets, front_image) @@ -479,7 +448,7 @@ def run(self): self.finished.emit() - def classify_batch(self, targets: List[Js08SimpleTarget], vista: QImage): + def classify_batch(self, targets: list, vista: QImage): """Discriminate image batch Parameters: @@ -498,9 +467,9 @@ def classify_batch(self, targets: List[Js08SimpleTarget], vista: QImage): for i, target in enumerate(targets): if i % self.batch_size == 0: data = np.zeros( - (self.batch_size, self.input_height, self.input_width, 3), + (self.batch_size, self.input_height, self.input_width, 3), dtype=np.float32 - ) + ) roi_image = target.clip_roi(vista) arr = target.img_to_arr(roi_image, self.input_width, self.input_height) @@ -522,4 +491,4 @@ def classify_batch(self, targets: List[Js08SimpleTarget], vista: QImage): if save_target_clip: postfix = 'pos' if target.discernment else 'neg' filename = f'{target.label}_{postfix}.png' - self.save_image(dir, filename, masked_img_list[i]) \ No newline at end of file + self.save_image(dir, filename, masked_img_list[i]) diff --git a/src/curve_save.py b/src/curve_save.py deleted file mode 100644 index 62bf6dd..0000000 --- a/src/curve_save.py +++ /dev/null @@ -1,124 +0,0 @@ -import itertools -import os - -import numpy as np -import pandas as pd - -import scipy -from scipy.optimize import curve_fit -from PyQt5 import QtWidgets, QtGui, QtCore - -curved_flag = True -# cam_name = cam_name -hanhwa_dist = [] -hanhwa_x = [] -hanhwa_r = [] -hanhwa_g = [] -hanhwa_b = [] -# epoch = epoch -# rgbsavedir = os.path.join(f"rgb/{cam_name}") -# extsavedir = os.path.join(f"extinction/{cam_name}") - -def select_max_rgb(r, g, b): - - select_color = "" - c_list = [r, g, b] - - c_index = c_list.index(max(c_list)) - - if c_index == 0: - select_color = "red" - elif c_index == 1: - select_color = "green" - else : - select_color = "blue" - return select_color - -def cal_curve(hanhwa: pd.DataFrame): - # hanhwa = pd.read_csv(f"{rgbsavedir}/{epoch}.csv") - print(hanhwa) - hanhwa = hanhwa.sort_values(by=['distance']) - hanhwa_dist = hanhwa[['distance']].squeeze().to_numpy() - hanhwa_x = np.linspace(hanhwa_dist[0], hanhwa_dist[-1], 100, endpoint=True) - hanhwa_x.sort() - hanhwa_r = hanhwa[['r']].squeeze().to_numpy() - hanhwa_g = hanhwa[['g']].squeeze().to_numpy() - hanhwa_b = hanhwa[['b']].squeeze().to_numpy() - - r1_init = hanhwa_r[0] * 0.7 - g1_init = hanhwa_g[0] * 0.7 - b1_init = hanhwa_b[0] * 0.7 - - r2_init = hanhwa_r[-1] * 1.3 - g2_init = hanhwa_g[-1] * 1.3 - b2_init = hanhwa_b[-1] * 1.3 - - select_color = select_max_rgb(r2_init, g2_init, b2_init) - - r_ext_init = [r1_init, r2_init, 1] - g_ext_init = [g1_init, g2_init, 1] - b_ext_init = [b1_init, b2_init, 1] - - try: - - hanhwa_opt_r, hanhwa_cov_r = curve_fit(func, hanhwa_dist, hanhwa_r, p0=r_ext_init, maxfev=5000) - hanhwa_opt_g, hanhwa_cov_g = curve_fit(func, hanhwa_dist, hanhwa_g, p0=g_ext_init, maxfev=5000) - hanhwa_opt_b, hanhwa_cov_b = curve_fit(func, hanhwa_dist, hanhwa_b, p0=b_ext_init, maxfev=5000) - - except Exception as e: - print("error msg: ", e) - return - list1 = [] - list2 = [] - list3 = [] - - list1.append(hanhwa_opt_r[0]) - list1.append(hanhwa_opt_g[0]) - list1.append(hanhwa_opt_b[0]) - - list2.append(hanhwa_opt_r[1]) - list2.append(hanhwa_opt_g[1]) - list2.append(hanhwa_opt_b[1]) - - list3.append(hanhwa_opt_r[2]) - list3.append(hanhwa_opt_g[2]) - list3.append(hanhwa_opt_b[2]) - - hanhwa_err_r = np.sqrt(np.diag(hanhwa_cov_r)) - hanhwa_err_g = np.sqrt(np.diag(hanhwa_cov_g)) - hanhwa_err_b = np.sqrt(np.diag(hanhwa_cov_b)) - - print_result(hanhwa_opt_r, hanhwa_opt_g, hanhwa_opt_b, hanhwa_err_r, hanhwa_err_g, hanhwa_err_b) - - print(f"Red channel: {extcoeff_to_vis(hanhwa_opt_r[2], hanhwa_err_r[2], 3)} km") - print(f"Green channel: {extcoeff_to_vis(hanhwa_opt_g[2], hanhwa_err_g[2], 3)} km") - print(f"Blue channel: {extcoeff_to_vis(hanhwa_opt_b[2], hanhwa_err_b[2], 3)} km") - - return list1, list2, list3, select_color - # update_extinc_signal.emit(list1, list2, list3, select_color) - - try: - os.mkdir(extsavedir) - except Exception as e: - pass - -# @staticmethod -def func(x, c1, c2, a): - return c2 + (c1 - c2) * np.exp(-a * x) - -def print_result(opt_r, opt_g, opt_b, err_r, err_g, err_b): - print(f"Red channel: (", - f"C1: {opt_r[0]:.2f} ± {err_r[0]:.2f}, ", - f"C2: {opt_r[1]:.2f} ± {err_r[1]:.2f}, ", - f"alpha: {opt_r[2]:.2f} ± {err_r[2]:.2f})") - print(f"Green channel: (", - f"C1: {opt_g[0]:.2f} ± {err_g[0]:.2f}, ", - f"C2: {opt_g[1]:.2f} ± {err_g[1]:.2f}, ", - f"alpha: {opt_g[2]:.2f} ± {err_g[2]:.2f})") - print(f"Blue channel: (", - f"C1: {opt_b[0]:.2f} ± {err_b[0]:.2f}, ", - f"C2: {opt_b[1]:.2f} ± {err_b[1]:.2f}, ", - f"alpha: {opt_b[2]:.2f} ± {err_b[2]:.2f})") - -def extcoeff_to_vis(optimal, error, coeff=3.291): - return coeff / (optimal + np.array((1, 0, -1)) * error) diff --git a/src/curve_thread.py b/src/curve_thread.py new file mode 100644 index 0000000..fa52782 --- /dev/null +++ b/src/curve_thread.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + +from multiprocessing import Queue + +from PyQt5.QtCore import QThread, pyqtSignal + + +class CurveThread(QThread): + poped = pyqtSignal(dict) + + def __init__(self, _q: Queue): + super().__init__() + self._run_flag = False + self.q = _q + + def run(self): + self._run_flag = True + while self._run_flag: + if not self.q.empty(): + visibility = self.q.get() + self.poped.emit(visibility) + # shut down capture system + + def stop(self): + """Sets run flag to False and waits for thread to finish""" + self._run_flag = False + self.quit() + self.wait() diff --git a/src/js08.py b/src/js08.py new file mode 100644 index 0000000..9eb232b --- /dev/null +++ b/src/js08.py @@ -0,0 +1,1136 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + +import sys +import os +import time +import random +import collections + +import vlc +import numpy as np +import pandas as pd +import multiprocessing as mp +from multiprocessing import Process, Queue + +from PyQt5.QtGui import (QPixmap, QIcon, QPainter, + QColor, QPen, QFont) +from PyQt5.QtWidgets import (QMainWindow, QWidget, QFrame, + QVBoxLayout, QLabel, QInputDialog, + QMessageBox) +from PyQt5.QtCore import (Qt, pyqtSlot, pyqtSignal, + QRect, QTimer, QObject, + QThread, QPointF, QDateTime, + QPoint) +from PyQt5.QtChart import (QChartView, QLegend, QLineSeries, + QPolarChart, QValueAxis, QChart, + QDateTimeAxis) +from PyQt5 import uic + +from login_view import LoginWindow +from video_thread_mp import producer +from js08_settings import JS08SettingWidget +from model import JS08Settings +from curve_thread import CurveThread +from clock import clockclock + +os.environ['VLC_VERBOSE'] = '-1' + + +def resource_path(relative_path: str): + """ + Get absolute path to resource, works for dev and for PyInstaller + + :param relative_path: Files to reference + :return: os.path.join + """ + try: + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath('.') + + return os.path.join(base_path, relative_path) + + +class Consumer(QThread): + poped = pyqtSignal(str) + + def __init__(self, q): + super().__init__() + self.q = q + self.running = True + + def run(self): + while self.running: + if not self.q.empty(): + data = q.get() + self.poped.emit(data) + + def pause(self): + self.running = False + + def resume(self): + self.running = True + + +class VisibilityView(QChartView): + + def __init__(self, parent: QWidget, maxlen: int): + super().__init__(parent) + self.setMinimumSize(200, 200) + self.setMaximumSize(600, 400) + + self.maxlen = maxlen + self.epoch = [] + self.vis = [] + self.flag = False + + now = QDateTime.currentSecsSinceEpoch() + str_now = str(now) + sequence = '0' + indicies = (10, 10) + # print(sequence.join([str_now[:indicies[0] - 1], str_now[indicies[1]:]])) + now = int(sequence.join([str_now[:indicies[0] - 1], str_now[indicies[1]:]])) + + current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now)) + year = current_time[:4] + md = current_time[5:7] + current_time[8:10] + # zeros = [(t * 1000, -1) for t in range(now - maxlen * 60, now, 60)] + # self.data = collections.deque(zeros, maxlen=maxlen) + + self.setRenderHint(QPainter.Antialiasing) + + chart = QChart(title='Visibility') + chart.legend().setVisible(False) + + self.setChart(chart) + self.series = QLineSeries() + chart.addSeries(self.series) + + axis_x = QDateTimeAxis() + axis_x.setFormat('hh:mm') + axis_x.setTitleText('Time') + + save_path = os.path.join(f'{JS08Settings.get("data_csv_path")}/PNM_9031RV_front/{year}') + file = f'{save_path}/{md}.csv' + if os.path.isfile(f'{file}') is False: + + zeros = [(t * 1000, -1) for t in range(now - maxlen * 60, now, 60)] + self.data = collections.deque(zeros, maxlen=maxlen) + + left = QDateTime.fromMSecsSinceEpoch(self.data[0][0]) + right = QDateTime.fromMSecsSinceEpoch(self.data[-1][0]) + # left = QDateTime.fromMSecsSinceEpoch(now - 3600 * 24) + # right = QDateTime.fromMSecsSinceEpoch(now * 1000) + axis_x.setRange(left, right) + chart.setAxisX(axis_x, self.series) + else: + + result = pd.read_csv(f'{save_path}/{md}.csv') + epoch = result['epoch'].tolist() + vis_list = result['visibility'].tolist() + + # data = [(t * 1000, -1) for t in range(now - maxlen * 60, now, 60)] + data = [] + + for i in range(len(epoch)): + # zeros = [(t * 1000, vis_list[i]) for t in range(now - maxlen * 60, now, 60)] + data.append((epoch[i] * 1000, vis_list[i])) + # if epoch[i] * 1000 in self.data[i]: + # print('!!!!!!!!!!') + # self.data.append((epoch[i] * 1000, vis_list[i])) + self.data = collections.deque(data, maxlen=maxlen) + # print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch[i]))) + + left = QDateTime.fromMSecsSinceEpoch(self.data[0][0]) + right = QDateTime.fromMSecsSinceEpoch(self.data[-1][0]) + axis_x.setRange(left, right) + chart.setAxisX(axis_x, self.series) + + axis_y = QValueAxis() + axis_y.setRange(0, 20) + # axis_y.setLabelsColor(QColor(255, 255, 255, 255)) + axis_y.setLabelFormat('%d') + axis_y.setTitleText('Distance (km)') + chart.setAxisY(axis_y, self.series) + + data_point = [QPointF(t, v) for t, v in self.data] + self.series.append(data_point) + + @pyqtSlot(int, list) + def refresh_stats(self, epoch: int, vis_list: list): + + # print(len(epoch), len(vis)) + + # now = QDateTime.currentSecsSinceEpoch() + + if len(vis_list) == 0: + vis_list = [0] + prev_vis = self.prevailing_visibility(vis_list) + self.data.append((epoch * 1000, prev_vis)) + + left = QDateTime.fromMSecsSinceEpoch(self.data[0][0]) + right = QDateTime.fromMSecsSinceEpoch(self.data[-1][0]) + # left = QDateTime.fromMSecsSinceEpoch(now - 3600 * 24 * 1000) + # right = QDateTime.fromMSecsSinceEpoch(now * 1000) + self.chart().axisX().setRange(left, right) + + data_point = [QPointF(t, v) for t, v in self.data] + self.series.replace(data_point) + + # for i in range(len(epoch)): + # # self.data.append((epoch[i], vis_list[i])) + # self.series.append(epoch[i], vis_list[i]) + + # self.series.replace([QPointF(1649725277.0, 5)]) + + # print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.data[-1][0] / 1000))) + + # self.axis_x.setRange(left, right) + # self.chart.setAxisX(self.axis_x, self.series) + + # print(self.flag) + # if self.flag is False: + # for i in range(len(epoch)): + # self.series.append(epoch[i], vis[i]) + # self.flag = True + # else: + # self.series.append(epoch[-1], vis[-1]) + + # self.epoch = epoch + # self.vis = vis + + # self.chart.removeSeries(self.series) + + # now = QDateTime.currentSecsSinceEpoch() + # zeros = [(t * 1000, -1) for t in range(now - self.maxlen * 60, now, 60)] + # self.data = collections.deque(zeros, maxlen=self.maxlen) + + # self.setRenderHint(QPainter.Antialiasing) + + # self.chart = QChart() + # self.chart.legend().setVisible(False) + # self.series.clear() + # self.series = QLineSeries() + + # if len(self.epoch) == 0: + # for i in range(len(self.epoch)): + # print(i) + # self.series.append(self.epoch[i], self.vis[i]) + # else: + # self.series.append(self.epoch[-1], self.vis[-1]) + # print(self.epoch[-1], self.vis[-1]) + + # print(epoch, vis) + # print(len(epoch), len(vis)) + + # self.setChart(self.chart) + # self.chart.addSeries(self.series) + + def prevailing_visibility(self, vis: list) -> float: + if None in vis: + return 0 + + sorted_vis = sorted(vis, reverse=True) + prevailing = sorted_vis[(len(sorted_vis) - 1) // 2] + + return prevailing + + def mouseDoubleClickEvent(self, event): + # view = GraphRangeView() + # view.exec_() + print('?') + + +class DiscernmentView(QChartView): + + def __init__(self, parent: QWidget): + super().__init__(parent) + self.setRenderHint(QPainter.Antialiasing) + self.setMinimumSize(200, 200) + self.setMaximumSize(600, 400) + + chart = QPolarChart(title='Discernment Visibility') + chart.legend().setAlignment(Qt.AlignRight) + chart.legend().setMarkerShape(QLegend.MarkerShapeCircle) + # chart.legend().setColor(QColor(255, 255, 255, 255)) + # chart.setTitleBrush(QColor(255, 255, 255, 255)) + # chart.setBackgroundBrush(QColor(0, 0, 0, 255)) + self.setChart(chart) + + # self.positives = QScatterSeries(name='Visibility') + # self.negatives = QScatterSeries(name='Negative') + # self.positives.setColor(QColor('green')) + # self.negatives.setColor(QColor('red')) + # self.positives.setMarkerSize(5) + # self.negatives.setMarkerSize(10) + # chart.addSeries(self.positives) + # chart.addSeries(self.negatives) + + self.series = QLineSeries() + self.series.setName('Visibility') + self.series.setColor(QColor('green')) + # self.series.append([QPointF(0, 0), QPoint(45, 10), QPoint(90, 5), QPointF(135, 5), + # QPointF(180, 10), QPointF(225, 10), QPointF(270, 15), QPointF(315, 15), + # QPointF(360, 0)]) + chart.addSeries(self.series) + + axis_x = QValueAxis() + axis_x.setTickCount(9) + # axis_x.setLabelsColor(QColor(255, 255, 255, 255)) + axis_x.setRange(0, 360) + axis_x.setLabelFormat('%d \xc2\xb0') + axis_x.setTitleText('Azimuth (deg)') + axis_x.setTitleVisible(False) + # chart.setAxisX(axis_x, self.positives) + # chart.setAxisX(axis_x, self.negatives) + chart.setAxisX(axis_x, self.series) + + axis_y = QValueAxis() + # axis_y.setLabelsColor(QColor(255, 255, 255, 255)) + axis_y.setRange(0, 20) + axis_y.setLabelFormat('%d km') + axis_y.setTitleText('Distance (km)') + axis_y.setTitleVisible(False) + # chart.setAxisY(axis_y, self.positives) + # chart.setAxisY(axis_y, self.negatives) + chart.setAxisY(axis_y, self.series) + + # self.refresh_stats() + + def refresh_stats(self, visibility: dict): + # az0 = 22.5 # 0 ~ 45° + # az1 = 67.5 # 45 ~ 90° + # az2 = 112.5 # 90 ~ 135° + # az3 = 157.5 # 135 ~ 180° + # az4 = 202.5 # 180 ~ 225° + # az5 = 247.5 # 225 ~ 270° + # az6 = 292.5 # 270 ~ 315° + # az7 = 337.5 # 315 ~ 360° + # + # positives = [] + # negatives = [(0, 4), (0, 9), (0, 14), + # (45, 5), (45, 10), (45, 15), + # (90, 5), (90, 10), (90, 15), + # (135, 5), (135, 10), (135, 15), + # (180, 5), (180, 10), (180, 15), + # (225, 5), (225, 10), (225, 15), + # (270, 5), (270, 10), (270, 15), + # (315, 5), (315, 10), (315, 15)] + # negatives = [(az0, 4), (az1, 7), (az2, 8), (az3, 6), (az4, 8), (az5, 7), (az6, 8), (az7, 9)] + # + # pos_point = [QPointF(a, d) for a, d in positives] + # self.positives.replace(pos_point) + # neg_point = [QPointF(a, d) for a, d in negatives] + # self.negatives.replace(neg_point) + + a = random.randint(0, 20) + b = random.randint(0, 20) + c = random.randint(0, 20) + d = random.randint(0, 20) + e = random.randint(0, 20) + f = random.randint(0, 20) + g = random.randint(0, 20) + h = random.randint(0, 20) + + self.series.replace([ + QPointF(0, float(visibility.get('front_N'))), QPointF(45, float(visibility.get('front_NE'))), + QPointF(90, float(visibility.get('front_E'))), QPointF(135, float(visibility.get('rear_SE'))), + QPointF(180, float(visibility.get('rear_S'))), QPointF(225, float(visibility.get('rear_SW'))), + QPointF(270, float(visibility.get('rear_W'))), QPointF(315, float(visibility.get('front_NW'))), + QPointF(360, float(visibility.get('front_N'))) + ]) + + def random_refresh(self): + # az0 = 22 # 0 ~ 45° + # az1 = 67 # 45 ~ 90° + # az2 = 112 # 90 ~ 135° + # az3 = 157 # 135 ~ 180° + # az4 = 202 # 180 ~ 225° + # az5 = 247 # 225 ~ 270° + # az6 = 292 # 270 ~ 315° + # az7 = 337 # 315 ~ 360° + + a = random.randint(0, 20) + b = random.randint(0, 20) + c = random.randint(0, 20) + d = random.randint(0, 20) + e = random.randint(0, 20) + f = random.randint(0, 20) + g = random.randint(0, 20) + h = random.randint(0, 20) + # print(a, b, c, d, e, f, g, h) + + self.series.replace([QPointF(0, a), QPointF(45, b), QPointF(90, c), QPointF(135, d), + QPointF(180, e), QPointF(225, f), QPointF(270, g), QPointF(315, h), + QPointF(360, a)]) + # self.series.replace([QPointF(0, 5), QPoint(45, 6), QPoint(90, 7), QPointF(135, 8), + # QPointF(180, 9), QPointF(225, 10), QPointF(270, 11), QPointF(315, 12), + # QPointF(360, 13)]) + + def mouseDoubleClickEvent(self, event): + print('ddable click76') + + +# class PolarPlot(QChartView): +# def __init__(self, parent: QWidget): +# super().__init__(parent) +# self.setRenderHint(QPainter.Antialiasing) +# self.setMinimumSize(200, 200) +# self.setMaximumSize(600, 400) +# +# # self.main_widget = QWidget() +# # self.setCentralWidget(self.main_widget) +# # +# chart = QChart() +# chart.legend().setVisible(False) +# # vbox = QVBoxLayout(self.main_widget) +# # vbox.addWidget() +# +# data = pd.DataFrame({'distance': [5, 10, 15, 20, 15, 10, 5, 10]}) +# compass = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] +# # index = [] +# +# dist = data['distance'] +# dist_list = dist.values.tolist() +# min_value = min(dist_list) +# +# # for i in range(len(dist_list)): +# # if dist_list[i] == min_value: +# # index.append(i) +# +# self.fig, self.ax = plt.subplots(1, 1, subplot_kw={'projection': 'polar'}) +# self.ax.set_theta_zero_location('N') +# self.ax.set_theta_direction(-1) +# +# # for i in range(len(dist_list)): +# # if i in index: +# # plt.bar(0.25 * np.pi * (1 + 2 * i) / 2, int(dist_list[i]), width=0.25 * np.pi, color=['red']) +# # else: +# # plt.bar(0.25 * np.pi * (1 + 2 * i) / 2, int(dist_list[i]), width=0.25 * np.pi, color=['green']) +# +# self.canvas = FigureCanvas(self.fig) +# self.canvas.resize(600, 400) +# # self.setChart(self.canvas) +# +# self.ax.set_xlabel('Visibility', fontsize=10) +# self.ax.set_rgrids(np.arange(0, 20, 5)) +# # self.ax.set_xticklabels(compass) +# self.ax.set_rorigin(-10) +# +# self.refresh_stats(dist_list) +# +# @pyqtSlot(list) +# def refresh_stats(self, distance: list): +# self.ax.clear() +# +# self.ax.set_theta_zero_location('N') +# self.ax.set_theta_direction(-1) +# self.ax.set_xlabel('Visibility', fontsize=10) +# +# # self.ax.set_rgrids(np.arange(0, 20, 5)) +# self.ax.set_rgrids([0, 5, 10, 15, 20]) +# self.ax.set_xticklabels(['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']) +# self.ax.set_rorigin(-10) +# +# # index = [] +# min_value = min(distance) +# +# for i in range(len(distance)): +# if distance[i] == min_value: +# # index.append(i) +# # if i in index: +# plt.bar(0.25 * np.pi * (1 + 2 * i) / 2, int(distance[i]), width=0.25 * np.pi, color=['red']) +# else: +# plt.bar(0.25 * np.pi * (1 + 2 * i) / 2, int(distance[i]), width=0.25 * np.pi, color=['green']) +# +# self.canvas.draw() +# +# def mouseDoubleClickEvent(self, event): +# print('hi22') + + +class ThumbnailView(QMainWindow): + + def __init__(self, image_file_name: str, date: int): + super().__init__() + + ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'resources/thumbnail_view.ui') + uic.loadUi(resource_path(ui_path), self) + + if os.path.isfile( + f'{JS08Settings.get("image_save_path")}/vista/PNM_9031RV_front/{date}/{image_file_name}.png') and \ + os.path.isfile(f'{JS08Settings.get("image_save_path")}/vista/PNM_9031RV_rear/{date}/{image_file_name}.png'): + + front_image = f'{JS08Settings.get("image_save_path")}/vista/PNM_9031RV_front/{date}/{image_file_name}.png' + rear_image = f'{JS08Settings.get("image_save_path")}/vista/PNM_9031RV_rear/{date}/{image_file_name}.png' + + self.front_image.setPixmap( + QPixmap(front_image).scaled(self.width(), self.height(), Qt.KeepAspectRatio)) + self.rear_image.setPixmap( + QPixmap(rear_image).scaled(self.width(), self.height(), Qt.KeepAspectRatio)) + + # self.front_image.setPixmap(QPixmap(front_image)) + # self.rear_image.setPixmap(QPixmap(rear_image)) + else: + print('no file') + + +class Js08MainWindow(QMainWindow): + + def __init__(self, q, _q): + super().__init__() + + # login_window = LoginWindow() + # login_window.exec_() + + ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'resources/main_window.ui') + uic.loadUi(resource_path(ui_path), self) + # self.showFullScreen() + + self._plot = VisibilityView(self, 1440) + # self._polar = PolarPlot(self) + self._polar = DiscernmentView(self) + + self.view = None + self.km_mile_convert = False + self.visibility = 0 + self.pm_text = 0 + self.year_date = None + self.epoch = [] + self.q_list = [] + self.q_list_scale = 1440 + self.data_date = [] + self.data_time = [] + self.data_vis = [] + + self.blank_label_front = QLabel() + self.blank_label_rear = QLabel() + self.blank_label_front.setStyleSheet('background-color: #FFFFFF') + self.blank_label_rear.setStyleSheet('background-color: #FFFFFF') + # self.blank_label_front.raise_() + # self.blank_label_rear.raise_() + + self.front_video_widget = VideoWidget(self) + self.front_video_widget.on_camera_change(JS08Settings.get('front_main')) + + self.rear_video_widget = VideoWidget(self) + self.rear_video_widget.on_camera_change(JS08Settings.get('rear_main')) + + self.video_horizontalLayout.addWidget(self.front_video_widget.video_frame) + self.video_horizontalLayout.addWidget(self.rear_video_widget.video_frame) + + self.graph_horizontalLayout.addWidget(self._plot) + self.polar_horizontalLayout.addWidget(self._polar) + + self.setting_button.enterEvent = self.btn_on + self.setting_button.leaveEvent = self.btn_off + + self.consumer = Consumer(q) + self.consumer.poped.connect(self.clock) + self.consumer.start() + + self.video_thread = CurveThread(_q) + self.video_thread.poped.connect(self.print_data) + self.video_thread.start() + + self.click_style = 'border: 1px solid red;' + + self.alert.clicked.connect(self.alert_test) + + self.c_vis_label.mousePressEvent = self.unit_convert + self.p_vis_label.mousePressEvent = self.test + + self.label_1hour_front.mouseDoubleClickEvent = self.thumbnail_click1_front + self.label_1hour_rear.mouseDoubleClickEvent = self.thumbnail_click1_rear + self.label_2hour_front.mouseDoubleClickEvent = self.thumbnail_click2_front + self.label_2hour_rear.mouseDoubleClickEvent = self.thumbnail_click2_rear + self.label_3hour_front.mouseDoubleClickEvent = self.thumbnail_click3_front + self.label_3hour_rear.mouseDoubleClickEvent = self.thumbnail_click3_rear + + self.setting_button.clicked.connect(self.setting_btn_click) + self.azimuth_button.clicked.connect(self.azimuth_btn_click) + # self.button.clicked.connect(self.button_click) + + self.show() + + def front_camera_pause(self, event): + self.front_video_widget.media_player.pause() + + def rear_camera_pause(self, event): + self.rear_video_widget.media_player.pause() + + def alert_test(self): + self.alert.setIcon(QIcon('resources/red.png')) + # a = [5, 10, 15, 20, 15, 10, 5, 10] + # random.shuffle(a) + # self._polar.refresh_stats(a) + + def reset_StyleSheet(self): + self.label_1hour_front.setStyleSheet('') + self.label_1hour_rear.setStyleSheet('') + self.label_2hour_front.setStyleSheet('') + self.label_2hour_rear.setStyleSheet('') + self.label_3hour_front.setStyleSheet('') + self.label_3hour_rear.setStyleSheet('') + + def thumbnail_view(self, file_name: str): + if self.isFullScreen(): + self.view = ThumbnailView(file_name, int(file_name[2:8])) + self.view.setGeometry(QRect(self.video_horizontalLayout.geometry().x(), + self.video_horizontalLayout.geometry().y() + 21, + self.video_horizontalLayout.geometry().width(), + self.video_horizontalLayout.geometry().height())) + self.view.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) + self.view.setWindowModality(Qt.ApplicationModal) + self.view.show() + self.view.raise_() + + else: + self.view = QMainWindow() + + def thumbnail_click1_front(self, e): + + name = self.label_1hour_time.text()[:2] + self.label_1hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_1hour_front.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_1hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_click1_rear(self, e): + name = self.label_1hour_time.text()[:2] + self.label_1hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_1hour_rear.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_1hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_click2_front(self, e): + name = self.label_2hour_time.text()[:2] + self.label_2hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_2hour_front.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_2hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_click2_rear(self, e): + name = self.label_2hour_time.text()[:2] + self.label_2hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_2hour_rear.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_2hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_click3_front(self, e): + name = self.label_3hour_time.text()[:2] + self.label_3hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_3hour_front.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_3hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_click3_rear(self, e): + name = self.label_3hour_time.text()[:2] + self.label_3hour_time.text()[3:] + epoch = time.strftime('%Y%m%d', time.localtime(time.time())) + self.thumbnail_view(epoch + name + '00') + + self.reset_StyleSheet() + self.label_3hour_rear.setStyleSheet(self.click_style) + self.monitoring_label.setText(f' {self.label_3hour_time.text()} image') + + QTimer.singleShot(5000, self.thumbnail_show) + + def thumbnail_show(self): + self.monitoring_label.setText(' Monitoring') + self.reset_StyleSheet() + self.view.close() + + @pyqtSlot() + def setting_btn_click(self): + self.front_video_widget.media_player.stop() + self.rear_video_widget.media_player.stop() + self.consumer.pause() + + dlg = JS08SettingWidget() + dlg.show() + dlg.setWindowModality(Qt.ApplicationModal) + dlg.exec_() + + self.front_video_widget.media_player.play() + self.rear_video_widget.media_player.play() + self.consumer.resume() + self.consumer.start() + + def azimuth_btn_click(self): + # self.front_video_widget.flag_status() + # self.rear_video_widget.flag_status() + # self.front_video_widget.update() + # self.rear_video_widget.update() + print(self.front_video_widget.media_player.video_get_width(0)) + print(self.front_video_widget.media_player.video_get_size()) + + def button_click(self): + pass + + def btn_on(self, event): + self.setting_button.setIcon(QIcon('resources/settings_on.png')) + + def btn_off(self, event): + self.setting_button.setIcon(QIcon('resources/settings.png')) + + def unit_convert(self, event): + if self.km_mile_convert: + self.km_mile_convert = False + elif self.km_mile_convert is False: + self.km_mile_convert = True + + def test(self, event): + print(self.front_video_widget.media_player.video_get_width(0)) + + def get_data(self, year, month_day): + """ + Prevailing visibility of front camera data + + :param year: Name of the year folder to access + :param month_day: Name of the month and day folder to access + :return: date, epoch, visibility in list type + + """ + + save_path = os.path.join(f'{JS08Settings.get("data_csv_path")}/PNM_9031RV_front/{year}') + + if os.path.isfile(f'{save_path}/{month_day}.csv'): + result = pd.read_csv(f'{save_path}/{month_day}.csv') + data_datetime = result['date'].tolist() + data_epoch = result['epoch'].tolist() + data_visibility = result['visibility'].tolist() + + return data_datetime, data_epoch, data_visibility + + else: + return [], [], [] + + @pyqtSlot(dict) + def print_data(self, visibility: dict): + + visibility_front = round(float(visibility.get('visibility_front')), 3) + visibility_rear = round(float(visibility.get('visibility_rear')), 3) + + epoch = QDateTime.currentSecsSinceEpoch() + current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch)) + year = current_time[:4] + md = current_time[5:7] + current_time[8:10] + + get_date, get_epoch, self.q_list = self.get_data(year, md) + + if len(self.q_list) == 0 or self.q_list_scale != len(self.q_list): + self.q_list = [] + get_date = [] + get_epoch = [] + for i in range(self.q_list_scale): + self.q_list.append(visibility_front) + result_vis = np.mean(self.q_list) + else: + self.q_list.pop(0) + self.q_list.append(visibility_front) + result_vis = np.mean(self.q_list) + + if len(self.data_date) >= self.q_list_scale or len(self.data_vis) >= self.q_list_scale: + self.data_date.pop(0) + self.data_time.pop(0) + self.data_vis.pop(0) + + self.data_date.append(current_time) + self.data_time.append(epoch) + self.data_vis.append(visibility_front) + + save_path_front = os.path.join(f'{JS08Settings.get("data_csv_path")}/PNM_9031RV_front/{year}') + save_path_rear = os.path.join(f'{JS08Settings.get("data_csv_path")}/PNM_9031RV_rear/{year}') + + file_front = f'{save_path_front}/{md}.csv' + file_rear = f'{save_path_rear}/{md}.csv' + + result_front = pd.DataFrame(columns=['date', 'epoch', 'visibility', 'W', 'NW', 'N', 'NE', 'E']) + result_rear = pd.DataFrame(columns=['date', 'epoch', 'visibility', 'E', 'SE', 'S', 'SW', 'W']) + + if os.path.isfile(f'{file_front}') is False or os.path.isfile(f'{file_rear}') is False: + os.makedirs(f'{save_path_front}', exist_ok=True) + os.makedirs(f'{save_path_rear}', exist_ok=True) + result_front.to_csv(f'{file_front}', mode='w', index=False) + result_rear.to_csv(f'{file_rear}', mode='w', index=False) + + result_front['date'] = [self.data_date[-1]] + result_front['epoch'] = [self.data_time[-1]] + result_front['visibility'] = [self.data_vis[-1]] + result_front['W'] = round(float(visibility.get('front_W')), 3) + result_front['NW'] = round(float(visibility.get('front_NW')), 3) + result_front['N'] = round(float(visibility.get('front_N')), 3) + result_front['NE'] = round(float(visibility.get('front_NE')), 3) + result_front['E'] = round(float(visibility.get('front_E')), 3) + result_front.to_csv(f'{file_front}', mode='a', index=False, header=False) + + result_rear['date'] = [self.data_date[-1]] + result_rear['epoch'] = [self.data_time[-1]] + result_rear['visibility'] = visibility_rear + result_rear['E'] = round(float(visibility.get('rear_E')), 3) + result_rear['SE'] = round(float(visibility.get('rear_SE')), 3) + result_rear['S'] = round(float(visibility.get('rear_S')), 3) + result_rear['SW'] = round(float(visibility.get('rear_SW')), 3) + result_rear['W'] = round(float(visibility.get('rear_W')), 3) + result_rear.to_csv(f'{file_rear}', mode='a', index=False, header=False) + + self.visibility = round(float(result_vis), 3) + + self._plot.refresh_stats(epoch, self.q_list) + self._polar.refresh_stats(visibility) + # self._polar.random_refresh() + # print(f'visibility: {visibility}') + + @pyqtSlot(str) + def clock(self, data): + self.blank_label_front.setGeometry(self.front_video_widget.geometry()) + self.blank_label_rear.setGeometry(self.rear_video_widget.geometry()) + + current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(data))) + self.year_date = current_time[2:4] + current_time[5:7] + current_time[8:10] + self.real_time_label.setText(current_time) + # self.p_vis_label.setText(f'{self.pm_text} ㎍/㎥') + + if self.visibility != 0: + ext = 3.912 / self.visibility + hd = 89 + self.pm_text = round((ext * 1000 / 4 / 2.5) / (1 + 5.67 * ((hd / 100) ** 5.8)), 2) + + if self.km_mile_convert: + self.c_vis_label.setText(f'{format(round(self.visibility / 1.609, 2), ",")} mile') + + elif self.km_mile_convert is False: + self.c_vis_label.setText(f'{format(int(self.visibility * 1000), ",")} m') + + if current_time[-1:] == '0': + self.thumbnail_refresh() + # self._plot.refresh_stats(QDateTime.currentSecsSinceEpoch(), self.q_list) + + if int(self.visibility * 1000) <= JS08Settings.get('visibility_alert_limit'): + self.alert.setIcon(QIcon('resources/red.png')) + else: + self.alert.setIcon(QIcon('resources/green.png')) + + def thumbnail_refresh(self): + + one_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600)) + two_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 2)) + three_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 3)) + # four_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 4)) + # five_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 5)) + # six_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 6)) + + # one_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60)) + # two_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60 * 2)) + # three_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60 * 3)) + # four_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60 * 4)) + # five_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60 * 5)) + # six_min_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 60 * 6)) + + self.label_1hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600))) + self.label_2hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 2))) + self.label_3hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 3))) + # self.label_4hour_time.setText(time.strftime('%H:%M', time.localtime(time.time(w) - 3600 * 4))) + # self.label_5hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 5))) + # self.label_6hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 6))) + + # self.label_1hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60))) + # self.label_2hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60 * 2))) + # self.label_3hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60 * 3))) + # self.label_4hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60 * 4))) + # self.label_5hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60 * 5))) + # self.label_6hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 60 * 6))) + + # self.label_1hour_front.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{one_min_ago}.jpg') + # .scaled(self.label_1hour_front.width(), self.label_1hour_front.height() - 5, Qt.IgnoreAspectRatio)) + # self.label_1hour_rear.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{one_min_ago}.jpg') + # .scaled(self.label_1hour_rear.width(), self.label_1hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + # self.label_2hour_front.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{two_min_ago}.jpg') + # .scaled(self.label_2hour_front.width(), self.label_2hour_front.height() - 5, Qt.IgnoreAspectRatio)) + # self.label_2hour_rear.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{two_min_ago}.jpg') + # .scaled(self.label_2hour_rear.width(), self.label_2hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + # self.label_3hour_front.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{three_min_ago}.jpg') + # .scaled(self.label_3hour_front.width(), self.label_3hour_front.height() - 5, Qt.IgnoreAspectRatio)) + # self.label_3hour_rear.setPixmap( + # QPixmap( + # f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{three_min_ago}.jpg') + # .scaled(self.label_3hour_rear.width(), self.label_3hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + + if os.path.isfile( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{one_hour_ago}.jpg'): + self.label_1hour_front.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{one_hour_ago}.jpg') + .scaled(self.label_1hour_front.width(), self.label_1hour_front.height() - 5, Qt.IgnoreAspectRatio)) + self.label_1hour_rear.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{one_hour_ago}.jpg') + .scaled(self.label_1hour_rear.width(), self.label_1hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + + if os.path.isfile( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{two_hour_ago}.jpg'): + self.label_2hour_front.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{two_hour_ago}.jpg') + .scaled(self.label_2hour_front.width(), self.label_2hour_front.height() - 5, Qt.IgnoreAspectRatio)) + self.label_2hour_rear.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{two_hour_ago}.jpg') + .scaled(self.label_2hour_rear.width(), self.label_2hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + + if os.path.isfile( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{three_hour_ago}.jpg'): + self.label_3hour_front.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{three_hour_ago}.jpg') + .scaled(self.label_3hour_front.width(), self.label_3hour_front.height() - 5, Qt.IgnoreAspectRatio)) + self.label_3hour_rear.setPixmap( + QPixmap( + f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_rear/{self.year_date}/{three_hour_ago}.jpg') + .scaled(self.label_3hour_rear.width(), self.label_3hour_rear.height() - 5, Qt.IgnoreAspectRatio)) + + # self.label_1hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{one_hour_ago}.jpg')) + # self.label_2hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{two_hour_ago}.jpg')) + # self.label_3hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{three_hour_ago}.jpg')) + # self.label_4hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{four_hour_ago}.jpg')) + # self.label_5hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{five_hour_ago}.jpg')) + # self.label_6hour.setPixmap( + # QPixmap(f'{JS08Settings.get("image_save_path")}/resize/PNM_9031RV_front/{self.year_date}/{six_hour_ago}.jpg')) + + def keyPressEvent(self, e): + """Override function QMainwindow KeyPressEvent that works when key is pressed""" + if e.key() == Qt.Key_F: + self.showFullScreen() + self.thumbnail_refresh() + if e.key() == Qt.Key_D: + self.showNormal() + self.thumbnail_refresh() + + def drawLines(self, qp): + pen = QPen(Qt.white, 1, Qt.DotLine) + + qp.setPen(pen) + qp.drawLine(240, 0, 240, 411) + qp.drawLine(480, 0, 480, 411) + qp.drawLine(720, 0, 720, 411) + + def closeEvent(self, event): + self.consumer.terminate() + self.video_thread.terminate() + print(f'Close time: {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}') + + +class VideoWidget(QWidget): + """Video stream player using QVideoWidget""" + + def __init__(self, parent: QObject = None): + super().__init__(parent) + + self.begin = QPoint() + self.end = QPoint() + self.flag = False + + args = [ + '--rtsp-frame-buffer-size=400000', + '--quiet', + # '--no-embedded-video', + # '--no-video-deco', + # '--no-qt-video-autoresize', + # '--no-autoscale', + # '--no-plugins-cache', + # '--mouse-hide-timeout=0', + # '--sout-x264-keyint=25', + # '--sout-x264-ref=1' + ] + + self.instance = vlc.Instance(args) + self.instance.log_unset() + + self.media_player = self.instance.media_player_new() + self.media_player.video_set_aspect_ratio('21:9') + + self.video_frame = QFrame() + self.blank_label = QLabel() + # self.blank_label.setStyleSheet('background-color: #83BCD4') + + if sys.platform == 'win32': + self.media_player.set_hwnd(self.video_frame.winId()) + + # layout = QVBoxLayout(self) + # layout.addWidget(self.video_frame) + + # self.video_frame.paintEvent = self.paint + + def flag_status(self): + if self.flag: + self.flag = False + elif self.flag is False: + self.flag = True + + def paint(self, event): + + qp = QPainter() + qp.begin(self) + self.drawLines(qp) + qp.end() + + def drawLines(self, qp): + + pen = QPen(Qt.white, 1, Qt.DotLine) + + qp.setPen(pen) + qp.setFont(QFont('Arial', 6)) + + if self.flag: + # if self.end.x() >= 600: + # self.end.setX(600) + # + # elif self.end.x() <= 360: + # self.end.setX(360) + + self.end.setX(360) + + qp.drawLine(self.width() * 0.125, 0, + self.width() * 0.125, self.height()) + qp.drawLine(self.width() * 0.375, 0, + self.width() * 0.375, self.height()) + qp.drawLine(self.width() * 0.625, 0, + self.width() * 0.625, self.height()) + qp.drawLine(self.width() * 0.875, 0, + self.width() * 0.875, self.height()) + + if self.geometry().x() == 0: + qp.drawText(self.width() * 0.0625, 7, 'W') + qp.drawText(self.width() * 0.25, 7, 'NW') + qp.drawText(self.width() * 0.5, 7, 'N') + qp.drawText(self.width() * 0.75, 7, 'NE') + qp.drawText(self.width() * 0.9375, 7, 'E') + + qp.drawText(self.width() * 0.0625, self.height() - 2, 'W') + qp.drawText(self.width() * 0.25, self.height() - 2, 'NW') + qp.drawText(self.width() * 0.5, self.height() - 2, 'N') + qp.drawText(self.width() * 0.75, self.height() - 2, 'NE') + qp.drawText(self.width() * 0.9375, self.height() - 2, 'E') + + elif self.geometry().x() == 960: + qp.drawText(self.width() * 0.0625, 7, 'E') + qp.drawText(self.width() * 0.25, 7, 'SE') + qp.drawText(self.width() * 0.5, 7, 'S') + qp.drawText(self.width() * 0.75, 7, 'SW') + qp.drawText(self.width() * 0.9375, 7, 'W') + + qp.drawText(self.width() * 0.0625, self.height() - 2, 'E') + qp.drawText(self.width() * 0.25, self.height() - 2, 'SE') + qp.drawText(self.width() * 0.5, self.height() - 2, 'S') + qp.drawText(self.width() * 0.75, self.height() - 2, 'SW') + qp.drawText(self.width() * 0.9375, self.height() - 2, 'W') + + @pyqtSlot(str) + def on_camera_change(self, uri: str): + if uri[:4] == 'rtsp': + self.media_player.set_media(self.instance.media_new(uri)) + self.media_player.play() + + else: + pass + + def mouseDoubleClickEvent(self, event): + print('하윙') + + +class MainCtrl(QObject): + front_camera_changed = pyqtSignal(str) + rear_camera_changed = pyqtSignal(str) + + def __init__(self): + super().__init__() + + self.front_target_prepared = False + self.rear_target_prepared = False + + self.front_camera_changed.connect(self.decompose_front_targets) + self.rear_camera_changed.connect(self.decompose_rear_targets) + + @pyqtSlot(str) + def decompose_front_targets(self, _: str): + self.front_target_prepared = False + self.decompose_targets('front') + self.front_target_prepared = True + + +if __name__ == '__main__': + try: + os.chdir(sys._MEIPASS) + except: + os.chdir(os.getcwd()) + + from PyQt5.QtWidgets import QApplication + + print(f'Start time: {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}') + + mp.freeze_support() + q = Queue() + _q = Queue() + + _producer = producer + + p = Process(name='clock', target=clockclock, args=(q,), daemon=True) + _p = Process(name='producer', target=_producer, args=(_q,), daemon=True) + + p.start() + _p.start() + + os.makedirs(f'{JS08Settings.get("data_csv_path")}', exist_ok=True) + os.makedirs(f'{JS08Settings.get("target_csv_path")}', exist_ok=True) + os.makedirs(f'{JS08Settings.get("image_save_path")}', exist_ok=True) + + app = QApplication(sys.argv) + screen_size = app.desktop().screenGeometry() + width, height = screen_size.width(), screen_size.height() + if width > 1920 or height > 1080: + QMessageBox.warning(None, 'Warning', 'JS08 is based on FHD screen.') + window = Js08MainWindow(q, _q) + sys.exit(app.exec()) diff --git a/src/js08_settings.py b/src/js08_settings.py new file mode 100644 index 0000000..421cba4 --- /dev/null +++ b/src/js08_settings.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + +import os +import cv2 +import time +import numpy as np +import pandas as pd +from scipy.optimize import curve_fit + +from PyQt5 import uic +from PyQt5.QtCore import (QPoint, QRect, Qt, + QTimer) +from PyQt5.QtGui import (QPixmap, QPainter, QBrush, + QColor, QPen, QImage, + QIcon, QFont) +from PyQt5.QtWidgets import (QApplication, QInputDialog, QDialog, + QMessageBox, QFileDialog, QHeaderView, + QTableWidget, QTableWidgetItem, QLabel) +from PyQt5.QtChart import QChart, QChartView, QLineSeries, QValueAxis + +from model import JS08Settings +import target_info +from auto_file_delete import FileAutoDelete + + +def resource_path(relative_path: str): + """ + Get absolute path to resource, works for dev and for PyInstaller + + :param relative_path: Files to reference + :return: os.path.join + """ + try: + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath('.') + + return os.path.join(base_path, relative_path) + + +class JS08SettingWidget(QDialog): + + def __init__(self): + + super().__init__() + ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'resources/setting_window.ui') + uic.loadUi(resource_path(ui_path), self) + self.showFullScreen() + self.setWindowFlag(Qt.FramelessWindowHint) + + self.begin = QPoint() + self.end = QPoint() + + self.upper_left = () + self.lower_right = () + + self.target_name = [] + self.left_range = [] + self.right_range = [] + self.distance = [] + self.azimuth = [] + + self.isDrawing = False + self.draw_flag = False + + self.video_width = 0 + self.video_height = 0 + + self.cp_image = None + self.end_drawing = None + + self.chart_view = None + self.select_target = None + self.select_target_flag = False + self.tableWidget.doubleClicked.connect(self.tableWidget_doubleClicked) + + self.r_list = [] + self.g_list = [] + self.b_list = [] + + self.current_camera = 'PNM_9030V' + self.cam_flag = False + + self.image_load() + + self.flag = 0 + # self.get_target(self.current_camera) + + # Add QChart Widget in value_verticalLayout + if len(self.left_range) > 0: + self.tableWidget.setEditTriggers(QTableWidget.NoEditTriggers) + self.show_target_table() + + if len(self.distance) > 4: + # self.chart_view = self.chart_draw() + # self.value_verticalLayout.addWidget(self.chart_view) + # self.chart_update() + pass + + self.red_checkBox.clicked.connect(self.chart_update) + self.green_checkBox.clicked.connect(self.chart_update) + self.blue_checkBox.clicked.connect(self.chart_update) + + self.flip_button.clicked.connect(self.camera_flip) + self.flip_button.enterEvent = self.btn_on + self.flip_button.leaveEvent = self.btn_off + + self.data_csv_path_button.clicked.connect(self.data_csv_path) + self.target_csv_path_button.clicked.connect(self.target_csv_path) + self.image_save_path_button.clicked.connect(self.image_save_path) + self.afd_button.clicked.connect(self.afd_btn_click) + + self.data_csv_path_textBrowser.setPlainText(JS08Settings.get('data_csv_path')) + self.target_csv_path_textBrowser.setPlainText(JS08Settings.get('target_csv_path')) + self.image_save_path_textBrowser.setPlainText(JS08Settings.get('image_save_path')) + + self.vis_limit_spinBox.setValue(JS08Settings.get('visibility_alert_limit')) + self.id_lineEdit.setText(JS08Settings.get('login_id')) + self.pw_lineEdit.setText(JS08Settings.get('login_pw')) + + self.image_size_comboBox.setCurrentIndex(JS08Settings.get('image_size')) + + self.image_label.paintEvent = self.lbl_paintEvent + self.image_label.mousePressEvent = self.lbl_mousePressEvent + self.image_label.mouseMoveEvent = self.lbl_mouseMoveEvent + self.image_label.mouseReleaseEvent = self.lbl_mouseReleaseEvent + + self.buttonBox.accepted.connect(self.accept_click) + self.buttonBox.rejected.connect(self.reject_click) + + def show_target_table(self): + min_x = [] + min_y = [] + self.r_list = [] + self.g_list = [] + self.b_list = [] + + copy_image = self.cp_image.copy() + row_count = len(self.distance) + self.tableWidget.setRowCount(row_count) + self.tableWidget.setColumnCount(3) + self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.tableWidget.setHorizontalHeaderLabels(['Number', 'Distance', 'Azimuth']) + + for upper_left, lower_right in zip(self.left_range, self.right_range): + result = target_info.minrgb(upper_left, lower_right, copy_image) + min_x.append(result[0]) + min_y.append(result[1]) + + self.r_list.append(copy_image[result[1], result[0], 0]) + self.g_list.append(copy_image[result[1], result[0], 1]) + self.b_list.append(copy_image[result[1], result[0], 2]) + + for i in range(0, row_count): + # crop_image = copy_image[min_y[i] - 50: min_y[i] + 50, min_x[i] - 50: min_x[i] + 50, :].copy() + # item = self.getImageLabel(crop_image) + # self.tableWidget.setCellWidget(i, 0, item) + + item2 = QTableWidgetItem(f'target {i + 1}') + item2.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter) + item2.setForeground(QBrush(QColor(255, 255, 255))) + self.tableWidget.setItem(i, 0, item2) + + item3 = QTableWidgetItem(f'{self.distance[i]} km') + item3.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter) + item3.setForeground(QBrush(QColor(255, 255, 255))) + self.tableWidget.setItem(i, 1, item3) + + item4 = QTableWidgetItem(f'{self.azimuth[i]}') + item4.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter) + item4.setForeground(QBrush(QColor(255, 255, 255))) + self.tableWidget.setItem(i, 2, item4) + + def tableWidget_doubleClicked(self, event): + self.select_target = self.tableWidget.currentIndex().row() + self.update() + + def getImageLabel(self, image): + imageLabel = QLabel() + imageLabel.setScaledContents(True) + height, width, channel = image.shape + bytesPerLine = channel * width + + qImage = QImage(image.data.tobytes(), 100, 100, bytesPerLine, QImage.Format_RGB888) + imageLabel.setPixmap(QPixmap.fromImage(qImage)) + + return imageLabel + + def func(self, x, c1, c2, a): + return c2 + (c1 - c2) * np.exp(-a * x) + + def chart_update(self): + + if self.chart_view is None: + self.chart_view = self.chart_draw() + + if self.value_verticalLayout.count() == 0: + self.chart_view = self.chart_draw() + self.value_verticalLayout.addWidget(self.chart_view) + else: + new_chart_view = self.chart_draw() + self.value_verticalLayout.removeWidget(self.chart_view) + self.value_verticalLayout.addWidget(new_chart_view) + self.value_verticalLayout.update() + self.chart_view = new_chart_view + + def chart_draw(self): + """세팅창 그래프 칸에 소산계수 차트를 그리는 함수""" + + try: + self.x = np.linspace(self.distance[0], self.distance[-1], 100, endpoint=True) + self.x.sort() + + hanhwa_opt_r, hanhwa_cov_r = curve_fit(self.func, self.distance, self.r_list, maxfev=5000) + hanhwa_opt_g, hanhwa_cov_g = curve_fit(self.func, self.distance, self.g_list, maxfev=5000) + hanhwa_opt_b, hanhwa_cov_b = curve_fit(self.func, self.distance, self.b_list, maxfev=5000) + + # chart object + chart = QChart() + font = QFont() + font.setPixelSize(20) + font.setBold(3) + # palette = QPalette() + # palette.setColor(QPalette.Window, QColor('red')) + chart.setTitleFont(font) + # chart.setPalette(palette) + chart.setTitle('Extinction coefficient Graph') + chart.setAnimationOptions(QChart.SeriesAnimations) + chart.setBackgroundBrush(QBrush(QColor(255, 255, 255))) + + axis_x = QValueAxis() + axis_x.setTickCount(7) + axis_x.setLabelFormat('%i') + axis_x.setTitleText('Distance(km)') + axis_x.setRange(0, 6) + chart.addAxis(axis_x, Qt.AlignBottom) + + axis_y = QValueAxis() + axis_y.setTickCount(7) + axis_y.setLabelFormat('%i') + axis_y.setTitleText('Intensity') + axis_y.setRange(0, 255) + chart.addAxis(axis_y, Qt.AlignLeft) + + # Red Graph + if self.red_checkBox.isChecked(): + series1 = QLineSeries() + series1.setName('Red') + pen = QPen() + pen.setWidth(2) + series1.setPen(pen) + series1.setColor(QColor('Red')) + + for dis in self.x: + series1.append(*(dis, self.func(dis, *hanhwa_opt_r))) + chart.addSeries(series1) # data feeding + series1.attachAxis(axis_x) + series1.attachAxis(axis_y) + + # Green Graph + if self.green_checkBox.isChecked(): + series2 = QLineSeries() + series2.setName('Green') + pen = QPen() + pen.setWidth(2) + series2.setPen(pen) + series2.setColor(QColor('Green')) + for dis in self.x: + series2.append(*(dis, self.func(dis, *hanhwa_opt_g))) + chart.addSeries(series2) # data feeding + + series2.attachAxis(axis_x) + series2.attachAxis(axis_y) + + # Blue Graph + if self.blue_checkBox.isChecked(): + series3 = QLineSeries() + series3.setName('Blue') + pen = QPen() + pen.setWidth(2) + series3.setPen(pen) + series3.setColor(QColor('Blue')) + for dis in self.x: + series3.append(*(dis, self.func(dis, *hanhwa_opt_b))) + chart.addSeries(series3) # data feeding + + series3.attachAxis(axis_x) + series3.attachAxis(axis_y) + + chart.legend().setAlignment(Qt.AlignRight) + + # displaying chart + chart.setBackgroundBrush(QBrush(QColor(22, 32, 42))) + chart_view = QChartView(chart) + chart_view.setRenderHint(QPainter.Antialiasing) + chart_view.setMaximumSize(800, 500) + + return chart_view + + except TypeError as e: + QMessageBox.about(self, 'Error', f'{e}') + pass + + def camera_flip(self): + if self.cam_flag: + self.cam_flag = False + else: + self.cam_flag = True + self.image_load() + + def btn_on(self, e): + self.flip_button.setIcon(QIcon('resources/flip_on.png')) + + def btn_off(self, e): + self.flip_button.setIcon(QIcon('resources/flip_off.png')) + + def image_load(self): + self.left_range = None + self.right_range = None + + if self.cam_flag: # cam_flag is True = PNM_9031RV_rear + src = JS08Settings.get('rear_camera_rtsp') + self.current_camera = JS08Settings.get('rear_camera_name') + self.target_setting_label.setText(f' Rear Target Setting ({self.current_camera})') + self.get_target(self.current_camera) + + else: # cam_flag is False = PNM_9031RV_front + src = JS08Settings.get('front_camera_rtsp') + self.current_camera = JS08Settings.get('front_camera_name') + self.target_setting_label.setText(f' Front Target Setting ({self.current_camera})') + self.get_target(self.current_camera) + + try: + # print(f'Current camera - {self.current_camera.replace("_", " ")}') + + # os.makedirs(f'{JS08Settings.get("target_csv_path")}/{self.current_camera}', exist_ok=True) + cap = cv2.VideoCapture(src) + ret, cv_img = cap.read() + cp_image = cv_img.copy() + cap.release() + + except Exception as e: + QMessageBox.about(self, 'Error', f'{e}') + + self.image_label.setPixmap(self.convert_cv_qt(cp_image)) + self.show_target_table() + self.chart_update() + + def convert_cv_qt(self, cv_img): + """Convert CV image to QImage.""" + cv_img = cv_img.copy() + cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB) + + self.cp_image = cv_img.copy() + + self.video_height, self.video_width, ch = cv_img.shape + + bytes_per_line = ch * self.video_width + convert_to_Qt_format = QImage(cv_img.data, self.video_width, self.video_height, + bytes_per_line, QImage.Format_RGB888) + p = convert_to_Qt_format.scaled(self.image_label.width(), + self.image_label.height(), + Qt.KeepAspectRatio, Qt.SmoothTransformation) + + return QPixmap.fromImage(p) + + def str_to_tuple(self, before_list): + """저장된 타겟들의 위치정보인 튜플 리스트가 문자열로 바뀌어 다시 튜플형태로 변환하는 함수""" + tuple_list = [i.split(',') for i in before_list] + tuple_list = [(int(i[0][1:]), int(i[1][:-1])) for i in tuple_list] + return tuple_list + + # 타겟 조정 및 썸네일 관련 함수 시작 + def thumbnail_pos(self, end_pos): + x = int((end_pos.x() / self.image_label.width()) * self.video_width) + y = int((end_pos.y() / self.image_label.height()) * self.video_height) + return x, y + + def thumbnail(self, image): + height, width, channel = image.shape + bytesPerLine = channel * width + qImg = QImage(image.data.tobytes(), width, height, bytesPerLine, QImage.Format_RGB888) + return qImg + + def lbl_paintEvent(self, event): + painter = QPainter(self.image_label) + + back_ground_image = self.thumbnail(self.cp_image) + bk_image = QPixmap.fromImage(back_ground_image) + painter.drawPixmap(QRect(0, 0, self.image_label.width(), + self.image_label.height()), bk_image) + + painter.setPen(QPen(Qt.white, 1, Qt.DotLine)) + + painter.drawLine((self.image_label.width() * 0.125), 0, + (self.image_label.width() * 0.125), self.image_label.height()) + painter.drawLine((self.image_label.width() * 0.375), 0, + (self.image_label.width() * 0.375), self.image_label.height()) + painter.drawLine((self.image_label.width() * 0.625), 0, + (self.image_label.width() * 0.625), self.image_label.height()) + painter.drawLine((self.image_label.width() * 0.875), 0, + (self.image_label.width() * 0.875), self.image_label.height()) + + if self.cam_flag: + if time.strftime('%Y%m%d', time.localtime(time.time())): + painter.setPen(QPen(Qt.black, 1, Qt.DotLine)) + painter.drawText(self.image_label.width() * 0.0625, 20, 'E') + painter.drawText(self.image_label.width() * 0.25, 20, 'SE') + painter.drawText(self.image_label.width() * 0.5, 20, 'S') + painter.drawText(self.image_label.width() * 0.75, 20, 'SW') + painter.drawText(self.image_label.width() * 0.9375, 20, 'W') + painter.setPen(QPen(Qt.white, 1, Qt.DotLine)) + elif self.cam_flag is False: + painter.drawText(self.image_label.width() * 0.0625, 20, 'W') + painter.drawText(self.image_label.width() * 0.25, 20, 'NW') + painter.drawText(self.image_label.width() * 0.5, 20, 'N') + painter.drawText(self.image_label.width() * 0.75, 20, 'NE') + painter.drawText(self.image_label.width() * 0.9375, 20, 'E') + + if self.left_range and self.right_range: + for name, corner1, corner2, in zip(self.target_name, self.left_range, self.right_range): + br = QBrush(QColor(100, 10, 10, 40)) + painter.setBrush(br) + painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) + corner1_1 = int(corner1[0] / self.video_width * self.image_label.width()) + corner1_2 = int(corner1[1] / self.video_height * self.image_label.height()) + corner2_1 = int((corner2[0] - corner1[0]) / self.video_width * self.image_label.width()) + corner2_2 = int((corner2[1] - corner1[1]) / self.video_height * self.image_label.height()) + painter.drawRect(QRect(corner1_1, corner1_2, corner2_1, corner2_2)) + painter.drawText(corner1_1 + corner2_1, corner1_2 - 5, f'{name[7:]}') + + # print(f'self.select_target: {self.select_target}') + + if self.isDrawing: + br = QBrush(QColor(100, 10, 10, 40)) + painter.setBrush(br) + painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) + painter.drawRect(QRect(self.begin, self.end)) + # th_x, th_y = self.thumbnail_pos(self.end) + # th_qImage = self.thumbnail(self.cp_image[th_y - 50:th_y + 50, th_x - 50:th_x + 50, :]) + # thumbnail_image = QPixmap.fromImage(th_qImage) + # painter.drawPixmap(QRect(self.end.x(), self.end.y(), 200, 200), thumbnail_image) + + if self.end_drawing: + painter.eraseRect(QRect(self.begin, self.end)) + painter.eraseRect(QRect(self.end.x(), self.end.y(), 200, 200)) + self.end_drawing = False + self.isDrawing = False + painter.end() + + def lbl_mousePressEvent(self, event): + """마우스 클릭시 발생하는 이벤트, QLabel method overriding""" + + # 좌 클릭시 실행 + if event.buttons() == Qt.LeftButton: + self.isDrawing = True + self.begin = event.pos() + self.end = event.pos() + self.upper_left = (int((self.begin.x() / self.image_label.width()) * self.video_width), + int((self.begin.y() / self.image_label.height()) * self.video_height)) + self.image_label.update() + + self.draw_flag = True + + # 우 클릭시 실행 + elif event.buttons() == Qt.RightButton: + self.isDrawing = False + + text, ok = QInputDialog.getText(self, 'Delete target', + 'Input target number to delete') + if ok: + if text == '': + del self.target_name[-1] + del self.left_range[-1] + del self.right_range[-1] + del self.distance[-1] + del self.azimuth[-1] + + else: + if len(self.left_range) > 0: + text = int(text) + del self.target_name[text - 1] + del self.left_range[text - 1] + del self.right_range[text - 1] + del self.distance[text - 1] + del self.azimuth[text - 1] + + self.update() + self.image_label.update() + self.draw_flag = False + + def lbl_mouseMoveEvent(self, event): + """마우스가 움직일 때 발생하는 이벤트, QLabel method overriding""" + if event.buttons() == Qt.LeftButton: + self.end = event.pos() + self.image_label.update() + self.isDrawing = True + + def lbl_mouseReleaseEvent(self, event): + """마우스 클릭이 떼질 때 발생하는 이벤트, QLabel method overriding""" + if self.draw_flag: + if self.cam_flag: + if 0 < self.upper_left[0] <= self.video_width * 0.125: + self.azimuth.append('E') + elif self.video_width * 0.125 < self.upper_left[0] <= self.video_width * 0.375: + self.azimuth.append('SE') + elif self.video_width * 0.375 < self.upper_left[0] <= self.video_width * 0.625: + self.azimuth.append('S') + elif self.video_width * 0.625 < self.upper_left[0] <= self.video_width * 0.875: + self.azimuth.append('SW') + elif self.video_width * 0.875 < self.upper_left[0] <= self.video_width: + self.azimuth.append('W') + + elif self.cam_flag is False: + if 0 < self.upper_left[0] <= self.video_width * 0.125: + self.azimuth.append('W') + elif self.video_width * 0.125 < self.upper_left[0] <= self.video_width * 0.375: + self.azimuth.append('NW') + elif self.video_width * 0.375 < self.upper_left[0] <= self.video_width * 0.625: + self.azimuth.append('N') + elif self.video_width * 0.625 < self.upper_left[0] <= self.video_width * 0.875: + self.azimuth.append('NE') + elif self.video_width * 0.875 < self.upper_left[0] <= self.video_width: + self.azimuth.append('E') + + self.end = event.pos() + self.image_label.update() + self.lower_right = (int((self.end.x() / self.image_label.width()) * self.video_width), + int((self.end.y() / self.image_label.height()) * self.video_height)) + + text, ok = QInputDialog.getText(self, '거리 입력', '거리(km)') + + if ok: + self.left_range.append(self.upper_left) + self.right_range.append(self.lower_right) + self.distance.append(text) + self.target_name.append('target_' + str(len(self.left_range))) + + # self.save_target(self.current_camera) + self.isDrawing = False + self.end_drawing = True + else: + self.isDrawing = False + del self.azimuth[-1] + + self.image_label.update() + + self.show_target_table() + + def data_csv_path(self): + fName = QFileDialog.getExistingDirectory( + self, 'Select path to save data csv file', JS08Settings.get('data_csv_path')) + if fName: + self.data_csv_path_textBrowser.setPlainText(fName) + else: + pass + + def target_csv_path(self): + fName = QFileDialog.getExistingDirectory( + self, 'Select path to save target csv file', JS08Settings.get('target_csv_path')) + if fName: + self.target_csv_path_textBrowser.setPlainText(fName) + else: + pass + + def image_save_path(self): + fName = QFileDialog.getExistingDirectory( + self, 'Select path to save image file', JS08Settings.get('image_save_path')) + if fName: + self.image_save_path_textBrowser.setPlainText(fName) + else: + pass + + def afd_btn_click(self): + dlg = FileAutoDelete() + dlg.show() + dlg.exec_() + + def save_vis(self): + + save_path_front = f'{JS08Settings.get("data_csv_path")}/{JS08Settings.get("front_camera_name")}/' \ + f'{JS08Settings.get("front_camera_name")}.csv' + + print(save_path_front) + + col = ['datetime', 'camera_direction', + 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', + 'prevailing_visibility'] + result = pd.DataFrame(col) + print(result) + + # result['datetime'] = + # result['camera_direction'] = + # result['N'] = + # result['NE'] = + # result['E'] = + # result['SE'] = + # result['S'] = + # result['SW'] = + # result['W'] = + # result['NW'] = + # result['prevailing_visibility'] = + + result.to_csv(f'{JS08Settings.get("data_csv_path")}/{self.current_camera}/{self.current_camera}.csv', + index=False) + + def save_target(self, camera: str): + + file = f'{JS08Settings.get("target_csv_path")}/{camera}/{camera}.csv' + if self.left_range and os.path.isfile(file): + col = ['target_name', 'left_range', 'right_range', 'distance', 'azimuth'] + result = pd.DataFrame(columns=col) + result['target_name'] = self.target_name + result['left_range'] = self.left_range + result['right_range'] = self.right_range + result['distance'] = self.distance + result['azimuth'] = self.azimuth + result.to_csv(file, mode='w', index=False) + print(f'[{camera}.csv SAVED]') + + def get_target(self, camera: str): + + save_path = os.path.join(f'{JS08Settings.get("target_csv_path")}/{camera}') + file = f'{save_path}/{camera}.csv' + + if os.path.isfile(f'{save_path}/{camera}.csv') is False: + os.makedirs(f'{save_path}', exist_ok=True) + makeFile = pd.DataFrame(columns=['target_name', 'left_range', 'right_range', 'distance', 'azimuth']) + makeFile.to_csv(file, mode='w', index=False) + + target_df = pd.read_csv(f'{save_path}/{camera}.csv') + self.target_name = target_df['target_name'].tolist() + self.left_range = self.str_to_tuple(target_df['left_range'].tolist()) + self.right_range = self.str_to_tuple(target_df['right_range'].tolist()) + self.distance = target_df['distance'].tolist() + self.azimuth = target_df['azimuth'].tolist() + + def accept_click(self): + + JS08Settings.set('data_csv_path', self.data_csv_path_textBrowser.toPlainText()) + JS08Settings.set('target_csv_path', self.target_csv_path_textBrowser.toPlainText()) + JS08Settings.set('image_save_path', self.image_save_path_textBrowser.toPlainText()) + JS08Settings.set('image_size', self.image_size_comboBox.currentIndex()) + JS08Settings.set('visibility_alert_limit', self.vis_limit_spinBox.value()) + JS08Settings.set('login_id', self.id_lineEdit.text()) + JS08Settings.set('login_pw', self.pw_lineEdit.text()) + + self.save_target(self.current_camera) + self.close() + + def reject_click(self): + self.close() + + +if __name__ == '__main__': + import sys + + app = QApplication(sys.argv) + ui = JS08SettingWidget() + ui.show() + sys.exit(app.exec_()) diff --git a/src/login_view.py b/src/login_view.py new file mode 100644 index 0000000..1de4647 --- /dev/null +++ b/src/login_view.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + +import os +import sys +import time + +from PyQt5.QtWidgets import QDialog +from PyQt5.QtCore import Qt +from PyQt5 import uic + +from model import JS08Settings + + +class LoginWindow(QDialog): + + def __init__(self): + super().__init__() + + ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'resources/login_window.ui') + uic.loadUi(ui_path, self) + self.setWindowFlag(Qt.WindowCloseButtonHint, False) + self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) + self.show() + + self.login_button.clicked.connect(self.login_click) + self.login_button.setShortcut('Return') + + self.id = JS08Settings.get('login_id') + self.pw = JS08Settings.get('login_pw') + + self.flag = 0 + + def login_click(self): + if self.id_lineEdit.text() == self.id and self.pw_lineEdit.text() == self.pw: + self.close() + else: + self.alert_label.setText('ID or P/W is not correct') + self.flag = self.flag + 1 + if self.flag >= 5: + self.alert_label.setText('P/W = 1 + 2 + 3 + 4') + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Escape: + pass + + +if __name__ == '__main__': + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + window = LoginWindow() + sys.exit(app.exec_()) diff --git a/src/logo.ico b/src/logo.ico new file mode 100644 index 0000000..da0d3d5 Binary files /dev/null and b/src/logo.ico differ diff --git a/src/model.py b/src/model.py index 4889d3c..ae61472 100644 --- a/src/model.py +++ b/src/model.py @@ -7,13 +7,20 @@ # 5jx2oh@gmail.com (Jongjin Oh) import os -from PyQt5.QtCore import QSettings, QStandardPaths +from PyQt5.QtCore import (QSettings, QRect) +from PyQt5.QtGui import (QImage) -class JS06Settings: - settings = QSettings('sijung', 'js06') +class JS08Settings: + settings = QSettings('sijung', 'js08') defaults = { + 'front_camera_name': 'PNM_9031RV_front', + 'front_camera_rtsp': 'rtsp://admin:sijung5520@192.168.100.131/profile2/media.smp', + 'front_main': 'rtsp://admin:sijung5520@192.168.100.131/profile5/media.smp', + 'rear_camera_name': 'PNM_9031RV_rear', + 'rear_camera_rtsp': 'rtsp://admin:sijung5520@192.168.100.132/profile2/media.smp', + 'rear_main': 'rtsp://admin:sijung5520@192.168.100.132/profile5/media.smp', 'data_csv_path': os.path.join('D:\\JS06', 'data'), 'target_csv_path': os.path.join('D:\\JS06', 'target'), 'image_save_path': os.path.join('D:\\JS06', 'image'), @@ -39,3 +46,18 @@ def get(cls, key): def restore_defaults(cls): for key, value in cls.defaults.items(): cls.set(key, value) + + +class JS06SimpleTarget: + + def __init__(self, + label: str, wedge: str, azimuth: float, + distance: float, roi: QRect, mask: QImage, + input_width: int, input_height: int): + super().__init__() + self.label = label + self.wedge = wedge + self.azimuth = azimuth + self.distance = distance + self.roi = roi + diff --git a/src/nd01.py b/src/nd01.py deleted file mode 100644 index 5bd57c5..0000000 --- a/src/nd01.py +++ /dev/null @@ -1,512 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021-2022 Sijung Co., Ltd. -# -# Authors: -# cotjdals5450@gmail.com (Seong Min Chae) -# 5jx2oh@gmail.com (Jongjin Oh) - - -import sys -import os -import time - -import vlc -import numpy as np -import pyqtgraph as pg -import multiprocessing as mp -from multiprocessing import Process, Queue - -from PyQt5.QtGui import (QPixmap, QIcon, QPainter, QColor) -from PyQt5.QtWidgets import (QMainWindow, QWidget, QGraphicsScene, - QFrame, QVBoxLayout) -from PyQt5.QtCore import (Qt, pyqtSlot, pyqtSignal, QRect, - QTimer, QObject, QThread) -from PyQt5.QtChart import (QChartView, QLegend, QLineSeries, - QPolarChart, QScatterSeries, QValueAxis) -from PyQt5 import uic - -from video_thread_mp import producer, VideoThread -from nd01_settings import ND01SettingWidget -from model import JS06Settings -from save_db import main - - -def clock(queue): - """Real-time clock - Current time to be expressed on JS-06 - - :param queue: MultiProcessing Queue - """ - while True: - now = str(time.time()) - queue.put(now) - time.sleep(1) - - -class Consumer(QThread): - poped = pyqtSignal(str) - - def __init__(self, q): - super().__init__() - self.q = q - self.running = True - - def run(self): - while self.running: - if not self.q.empty(): - data = q.get() - self.poped.emit(data) - - def pause(self): - self.running = False - - def resume(self): - self.running = True - - -class TimeAxisItem(pg.AxisItem): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.setLabel(text='Time (sec)', units=None) - self.enableAutoSIPrefix(False) - - def tickStrings(self, values, scale, spacing): - """ - override 하여, tick 옆에 써지는 문자를 원하는대로 수정함. - values --> x축 값들 - 숫자로 이루어진 Iterable data(하나씩 차례로 반환 가능한 object -> ex) List[int]) list, str, tuple 등등 - """ - return [time.strftime("%H:%M:%S", time.localtime(local_time)) for local_time in values] - - -class PlotWidget(QWidget): - - def __init__(self, parent=None): - QWidget.__init__(self, parent) - - self.pw = pg.PlotWidget( - labels={'left': 'Visibility (km)'}, - axisItems={'bottom': TimeAxisItem(orientation='bottom')} - ) - - self.pw.showGrid(x=True, y=True) - self.pdi = self.pw.plot(pen='w') # PlotDataItem obj 반환. - - self.p1 = self.pw.plotItem - - self.p2 = pg.ViewBox() - self.p1.showAxis('right') - self.p1.scene().addItem(self.p2) - self.p1.getAxis('right').linkToView(self.p2) - self.p2.setXLink(self.p1) - self.p1.getAxis('right').setLabel('Axis 2', color='#ffff00') - - self.p2.setGeometry(self.p1.vb.sceneBoundingRect()) - # self.p2.addItem(pg.PlotCurveItem([10, 20, 40, 80, 400, 2000], pen='y')) - # self.pdi.sigClicked.connect(self.onclick) - - self.plotData = {'x': [], 'y': []} - self.pre_plotData = {'x': [], 'y': []} - - def update_plot(self, new_time_data: int): - self.plotData['x'].append(new_time_data) - self.plotData['y'].append(np.random.randint(10000, 15000)) - - # 항상 x축 시간을 설정한 범위 ( -3 시간 전 ~ 10 분 후 )만 보여줌. - # self.pw.setXRange(new_time_data - 3600 * 3, new_time_data + 600, padding=0) - # self.pw.setYRange(-1, 21, padding=0) - - data = [] - for i in self.plotData['y']: - i = i / 1000 - data.append(i) - self.pdi.setData(self.plotData['x'], data) - - # self.pdi.setData([1643873584, 1643873585, 1643873586, 1643873587, 1643873588], - # [12, 11, 10, 14, 13]) - - def onclick(self, plot, points): - for point in points: - print(point.pos()) - - -class PolarWidget(QWidget): - - def __init__(self, parent=None): - QWidget.__init__(self, parent) - - self.pw = pg.plot( - labels={'left': 'Visibility (km)'}, - axisItems={'bottom': TimeAxisItem(orientation='bottom')} - ) - - self.pw.showGrid(x=True, y=True) - self.pdi = self.pw.plot(pen='w') - - self.pw.addLine(x=0, pen=0.2) - self.pw.addLine(y=0, pen=0.2) - for r in range(2, 20, 2): - circle = pg.QtGui.QGraphicsEllipseItem(-r, -r, r * 2, r * 2) - circle.setPen(pg.mkPen(0.2)) - self.pw.addItem(circle) - - theta = np.linspace(0, 2 * np.pi, 8) - radius = np.random.normal(loc=10, size=8) - - x = radius * np.cos(theta) - y = radius * np.sin(theta) - self.pw.plot(x, y) - - -class ThumbnailView(QMainWindow): - - def __init__(self, image_file_name: str, date: int): - super().__init__() - - ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - "ui/thumbnail_view.ui") - uic.loadUi(ui_path, self) - - self.front_image.setPixmap(QPixmap(f'D:/ND-01/vista/{date}/{image_file_name}.png') - .scaled(self.front_image.width(), self.front_image.height())) - self.rear_image.setPixmap(QPixmap(f'D:/ND-01/vista/{date}/{image_file_name}.png') - .scaled(self.rear_image.width(), self.rear_image.height())) - - -class ND01MainWindow(QMainWindow): - - def __init__(self, q): - super().__init__() - - ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - "resources/main_window.ui") - uic.loadUi(ui_path, self) - self.showFullScreen() - self._plot = PlotWidget() - self._polar = PolarWidget() - self.view = None - self.km_mile_convert = False - self.date = None - - self.front_video_widget = VideoWidget(self) - self.front_video_widget.on_camera_change("rtsp://admin:sijung5520@192.168.100.101/profile2/media.smp") - - self.rear_video_widget = VideoWidget(self) - self.rear_video_widget.on_camera_change("rtsp://admin:sijung5520@192.168.100.100/profile2/media.smp") - - self.video_horizontalLayout.addWidget(self.front_video_widget) - self.video_horizontalLayout.addWidget(self.rear_video_widget) - - self.scene = QGraphicsScene() - self.vis_plot.setScene(self.scene) - self.plotWidget = self._plot.pw - self.plotWidget.resize(600, 400) - self.scene.addWidget(self.plotWidget) - - self.scene_polar = QGraphicsScene() - self.polar_plot.setScene(self.scene_polar) - self.polarWidget = self._polar.pw - self.polarWidget.resize(600, 400) - self.scene_polar.addWidget(self.polarWidget) - - self.setting_button.enterEvent = self.btn_on - self.setting_button.leaveEvent = self.btn_off - - self.consumer = Consumer(q) - self.consumer.poped.connect(self.clock) - self.consumer.start() - - self.click_style = 'border: 1px solid red;' - - self.alert.clicked.connect(self.alert_test) - - self.c_vis_label.mousePressEvent = self.unit_convert - self.p_vis_label.mousePressEvent = self.unit_convert - - self.label_1hour.mouseDoubleClickEvent = self.thumbnail_click1 - self.label_2hour.mouseDoubleClickEvent = self.thumbnail_click2 - self.label_3hour.mouseDoubleClickEvent = self.thumbnail_click3 - self.label_4hour.mouseDoubleClickEvent = self.thumbnail_click4 - self.label_5hour.mouseDoubleClickEvent = self.thumbnail_click5 - self.label_6hour.mouseDoubleClickEvent = self.thumbnail_click6 - - self.setting_button.clicked.connect(self.setting_btn_click) - - self.show() - - def alert_test(self): - self.alert.setIcon(QIcon('resources/asset/red.png')) - - def reset_StyleSheet(self): - self.label_1hour.setStyleSheet('') - self.label_2hour.setStyleSheet('') - self.label_3hour.setStyleSheet('') - self.label_4hour.setStyleSheet('') - self.label_5hour.setStyleSheet('') - self.label_6hour.setStyleSheet('') - - def thumbnail_view(self, file_name: str): - self.view = ThumbnailView(file_name, self.date) - self.view.setGeometry(QRect(self.video_horizontalLayout.geometry().x(), - self.video_horizontalLayout.geometry().y() + 21, - self.video_horizontalLayout.geometry().width(), - self.video_horizontalLayout.geometry().height())) - self.view.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint) - self.view.setWindowModality(Qt.ApplicationModal) - self.view.show() - - def thumbnail_click1(self, e): - name = self.label_1hour_time.text()[:2] + self.label_1hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_1hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_1hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_click2(self, e): - name = self.label_2hour_time.text()[:2] + self.label_2hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_2hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_2hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_click3(self, e): - name = self.label_3hour_time.text()[:2] + self.label_3hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_3hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_3hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_click4(self, e): - name = self.label_4hour_time.text()[:2] + self.label_4hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_4hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_4hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_click5(self, e): - name = self.label_5hour_time.text()[:2] + self.label_5hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_5hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_5hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_click6(self, e): - name = self.label_6hour_time.text()[:2] + self.label_6hour_time.text()[3:] - epoch = time.strftime("%Y%m%d", time.localtime(time.time())) - self.thumbnail_view(epoch + name + "00") - - self.reset_StyleSheet() - self.label_6hour.setStyleSheet(self.click_style) - self.monitoring_label.setText(f' {self.label_6hour_time.text()} image') - - QTimer.singleShot(5000, self.thumbnail_show) - - def thumbnail_show(self): - self.monitoring_label.setText(' Monitoring') - self.reset_StyleSheet() - self.view.close() - - @pyqtSlot() - def setting_btn_click(self): - self.front_video_widget.media_player.stop() - self.rear_video_widget.media_player.stop() - self.consumer.pause() - - dlg = ND01SettingWidget() - dlg.show() - dlg.setWindowModality(Qt.ApplicationModal) - dlg.exec_() - - self.front_video_widget.media_player.play() - self.rear_video_widget.media_player.play() - self.consumer.resume() - self.consumer.start() - - def btn_on(self, event): - self.setting_button.setIcon(QIcon('resources/asset/settings_on.png')) - - def btn_off(self, event): - self.setting_button.setIcon(QIcon('resources/asset/settings.png')) - - def unit_convert(self, event): - if self.km_mile_convert: - self.km_mile_convert = False - elif self.km_mile_convert is False: - self.km_mile_convert = True - - @pyqtSlot(str) - def clock(self, data): - current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(data))) - self.date = current_time[2:4] + current_time[5:7] - self.real_time_label.setText(current_time) - self._plot.update_plot(int(float(data))) - - result = 0 - for i in self._plot.plotData['y']: - result += i - p_vis_km = f'{format(round(int(result / len(self._plot.plotData["y"])), 2), ",")}' - p_vis_nm = f'{format(round(int(result / len(self._plot.plotData["y"])) / 1609, 2), ",")}' - - if self.km_mile_convert: - self.c_vis_label.setText(f'{format(round(self._plot.plotData["y"][-1] / 1609, 2), ",")} mile') - self.p_vis_label.setText(f'{p_vis_nm} mile') - - elif self.km_mile_convert is False: - self.c_vis_label.setText(f'{format(self._plot.plotData["y"][-1], ",")} m') - self.p_vis_label.setText(f'{p_vis_km} m') - - data_time = self._plot.plotData['x'] - if int(float(data)) - 3600 * 3 in self._plot.plotData['x']: - index = data_time.index(int(float(data)) - 3600 * 3) - self._plot.plotData['x'].pop(index) - self._plot.plotData['y'].pop(index) - - self.thumbnail_refresh() - if current_time[-2:] == "00": - self.thumbnail_refresh() - - if int(p_vis_km.replace(',', '')) <= JS06Settings.get('visibility_alert_limit'): - self.alert.setIcon(QIcon('resources/asset/red.png')) - - def thumbnail_refresh(self): - one_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600)) - two_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 2)) - three_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 3)) - four_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 4)) - five_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 5)) - six_hour_ago = time.strftime('%Y%m%d%H%M00', time.localtime(time.time() - 3600 * 6)) - - self.label_1hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600))) - self.label_2hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 2))) - self.label_3hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 3))) - self.label_4hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 4))) - self.label_5hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 5))) - self.label_6hour_time.setText(time.strftime('%H:%M', time.localtime(time.time() - 3600 * 6))) - - self.label_1hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{one_hour_ago}.jpg')) - self.label_2hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{two_hour_ago}.jpg')) - self.label_3hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{three_hour_ago}.jpg')) - self.label_4hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{four_hour_ago}.jpg')) - self.label_5hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{five_hour_ago}.jpg')) - self.label_6hour.setPixmap( - QPixmap(f'{JS06Settings.get("image_save_path")}/resize/{self.date}/{six_hour_ago}.jpg')) - - def keyPressEvent(self, e): - """Override function QMainwindow KeyPressEvent that works when key is pressed""" - if e.key() == Qt.Key_F: - self.showFullScreen() - if e.key() == Qt.Key_D: - self.showNormal() - - -class VideoWidget(QWidget): - """Video stream player using QVideoWidget""" - video_frame = None - - def __init__(self, parent: QObject = None): - super().__init__(parent) - - args = [ - "--rtsp-frame-buffer-size", - "1000000" - ] - - self.instance = vlc.Instance(args) - self.instance.log_unset() - self.media_player = self.instance.media_player_new() - - self.image_player = self.instance.media_list_player_new() - self.image_media = self.instance.media_list_new('') - - self.video_frame = QFrame() - - if sys.platform == 'win32': - self.media_player.set_hwnd(self.video_frame.winId()) - - layout = QVBoxLayout(self) - layout.addWidget(self.video_frame) - - @pyqtSlot(str) - def on_camera_change(self, uri: str): - # uri = "rtsp://admin:sijung5520@192.168.100.100/profile2/media.smp" - if uri[:4] == "rtsp": - self.media_player.set_media(self.instance.media_new(uri)) - self.media_player.play() - else: - pass - - -class MainCtrl(QObject): - front_camera_changed = pyqtSignal(str) - rear_camera_changed = pyqtSignal(str) - - def __init__(self): - super().__init__() - - self.front_target_prepared = False - self.rear_target_prepared = False - - self.front_camera_changed.connect(self.decompose_front_targets) - self.rear_camera_changed.connect(self.decompose_rear_targets) - - @pyqtSlot(str) - def decompose_front_targets(self, _: str): - self.front_target_prepared = False - self.decompose_targets('front') - self.front_target_prepared = True - - -if __name__ == '__main__': - - from PyQt5.QtWidgets import QApplication - - mp.freeze_support() - q = Queue() - _q = Queue() - - _producer = producer - - p = Process(name='clock', target=clock, args=(q, ), daemon=True) - _p = Process(name="producer", target=_producer, args=(_q, ), daemon=True) - - p.start() - _p.start() - - os.makedirs('D:/ND-01/vista', exist_ok=True) - os.makedirs('D:/ND-01/resize', exist_ok=True) - - app = QApplication(sys.argv) - window = ND01MainWindow(q) - sys.exit(app.exec_()) - - # MainWindow = QMainWindow() - # ui = ND01MainWindow() - # ui.show() - # sys.exit(app.exec_()) diff --git a/src/nd01_settings.py b/src/nd01_settings.py deleted file mode 100644 index 9d8e2b7..0000000 --- a/src/nd01_settings.py +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021-2022 Sijung Co., Ltd. -# -# Authors: -# cotjdals5450@gmail.com (Seong Min Chae) -# 5jx2oh@gmail.com (Jongjin Oh) - - -import os - -import cv2 -import pandas as pd -from PyQt5 import uic -from PyQt5.QtCore import (QPoint, QRect, Qt) -from PyQt5.QtGui import (QPixmap, QPainter, QBrush, - QColor, QPen, QImage, - QIcon) -from PyQt5.QtWidgets import (QApplication, QLabel, QInputDialog, - QDialog, QAbstractItemView, QVBoxLayout, - QGridLayout, QPushButton, QMessageBox, - QFileDialog) -from model import JS06Settings - - -class ND01SettingWidget(QDialog): - - def __init__(self, *args, **kwargs): - - super().__init__(*args, **kwargs) - ui_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - "resources/settings.ui") - uic.loadUi(ui_path, self) - # self.setWindowFlag(Qt.FramelessWindowHint) - - self.begin = QPoint() - self.end = QPoint() - self.upper_left = () - self.lower_right = () - self.left_range = [] - self.right_range = [] - self.distance = [] - self.target_name = [] - self.min_xy = () - - self.isDrawing = False - self.draw_flag = False - self.cam_flag = False - - self.video_width = 0 - self.video_height = 0 - - self.cp_image = None - self.end_drawing = None - - self.current_camera = "" - - self.image_load() - - # 그림 그리는 삐뮤디 생성 - # self.blank_lbl = QLabel(self.image_label) - # # self.blank_lbl.setGeometry(0, 0, 1200, 500) - # self.blank_lbl.paintEvent = self.lbl_paintEvent - # - # self.blank_lbl.mousePressEvent = self.lbl_mousePressEvent - # self.blank_lbl.mouseMoveEvent = self.lbl_mouseMoveEvent - # self.blank_lbl.mouseReleaseEvent = self.lbl_mouseReleaseEvent - - self.flip_button.clicked.connect(self.camera_flip) - self.flip_button.enterEvent = self.btn_on - self.flip_button.leaveEvent = self.btn_off - - self.data_csv_path_button.clicked.connect(self.data_csv_path) - self.target_csv_path_button.clicked.connect(self.target_csv_path) - self.image_save_path_button.clicked.connect(self.image_save_path) - - self.data_csv_path_textBrowser.setPlainText(JS06Settings.get('data_csv_path')) - self.target_csv_path_textBrowser.setPlainText(JS06Settings.get('target_csv_path')) - self.image_save_path_textBrowser.setPlainText(JS06Settings.get('image_save_path')) - - self.vis_limit_spinBox.setValue(JS06Settings.get('visibility_alert_limit')) - self.id_lineEdit.setText(JS06Settings.get('login_id')) - self.pw_lineEdit.setText(JS06Settings.get('login_pw')) - - self.image_size_comboBox.setCurrentIndex(JS06Settings.get('image_size')) - # self.image_size_comboBox.currentTextChanged.connect(self.image_size_changed) - - self.image_label.paintEvent = self.lbl_paintEvent - self.image_label.mousePressEvent = self.lbl_mousePressEvent - self.image_label.mouseMoveEvent = self.lbl_mouseMoveEvent - self.image_label.mouseReleaseEvent = self.lbl_mouseReleaseEvent - - self.get_target() - - self.buttonBox.accepted.connect(self.accept_click) - self.buttonBox.rejected.connect(self.reject) - - def camera_flip(self): - if self.cam_flag: - self.cam_flag = False - else: - self.cam_flag = True - self.image_load() - - def btn_on(self, e): - self.flip_button.setIcon(QIcon('resources/asset/flip_on.png')) - - def btn_off(self, e): - self.flip_button.setIcon(QIcon('resources/asset/flip_off.png')) - - def image_load(self): - # self.image_label.update() - - if self.cam_flag: - src = "rtsp://admin:sijung5520@192.168.100.100/profile2/media.smp" - self.target_setting_label.setText(' Rear Target Setting') - self.current_camera = 'PNM_9030V' - self.get_target() - - else: - src = "rtsp://admin:sijung5520@192.168.100.101/profile2/media.smp" - self.target_setting_label.setText(' Front Target Setting') - self.current_camera = 'PNM_9022V' - self.get_target() - - try: - print(f'현재 카메라 {self.current_camera}') - - os.makedirs(f'{JS06Settings.get("target_csv_path")}/{self.current_camera}', exist_ok=True) - cap = cv2.VideoCapture(src) - ret, cv_img = cap.read() - cp_image = cv_img.copy() - cap.release() - except Exception as e: - QMessageBox.about(self, 'Error', f'{e}') - - self.image_label.setPixmap(self.convert_cv_qt(cp_image)) - - def convert_cv_qt(self, cv_img): - """Convert CV image to QImage.""" - cv_img = cv_img.copy() - cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB) - - self.cp_image = cv_img.copy() - - self.video_height, self.video_width, ch = cv_img.shape - - bytes_per_line = ch * self.video_width - convert_to_Qt_format = QImage(cv_img.data, self.video_width, self.video_height, - bytes_per_line, QImage.Format_RGB888) - p = convert_to_Qt_format.scaled(self.image_label.width(), - self.image_label.height(), - Qt.KeepAspectRatio, Qt.SmoothTransformation) - - return QPixmap.fromImage(p) - - def lbl_paintEvent(self, event): - painter = QPainter(self.image_label) - - back_ground_image = self.thumbnail(self.cp_image) - bk_image = QPixmap.fromImage(back_ground_image) - painter.drawPixmap(QRect(0, 0, self.image_label.width(), - self.image_label.height()), bk_image) - - for corner1, corner2, in zip(self.left_range, self.right_range): - br = QBrush(QColor(100, 10, 10, 40)) - painter.setBrush(br) - painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) - corner1_1 = int(corner1[0] / self.video_width * self.image_label.width()) - corner1_2 = int(corner1[1] / self.video_height * self.image_label.height()) - corner2_1 = int((corner2[0] - corner1[0]) / self.video_width * self.image_label.width()) - corner2_2 = int((corner2[1] - corner1[1]) / self.video_height * self.image_label.height()) - painter.drawRect(QRect(corner1_1, corner1_2, corner2_1, corner2_2)) - - if self.isDrawing: - br = QBrush(QColor(100, 10, 10, 40)) - painter.setBrush(br) - painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) - painter.drawRect(QRect(self.begin, self.end)) - th_x, th_y = self.thumbnail_pos(self.end) - th_qImage = self.thumbnail(self.cp_image[th_y - 50:th_y + 50, th_x - 50:th_x + 50, :]) - thumbnail_image = QPixmap.fromImage(th_qImage) - painter.drawPixmap(QRect(self.end.x(), self.end.y(), 200, 200), thumbnail_image) - - if self.end_drawing: - painter.eraseRect(QRect(self.begin, self.end)) - painter.eraseRect(QRect(self.end.x(), self.end.y(), 200, 200)) - self.end_drawing = False - self.isDrawing = False - painter.end() - - def str_to_tuple(self, before_list): - """저장된 타겟들의 위치정보인 튜플 리스트가 문자열로 바뀌어 다시 튜플형태로 변환하는 함수""" - tuple_list = [i.split(',') for i in before_list] - tuple_list = [(int(i[0][1:]), int(i[1][:-1])) for i in tuple_list] - return tuple_list - - # 타겟 조정 및 썸네일 관련 함수 시작 - def thumbnail_pos(self, end_pos): - x = int((end_pos.x() / self.image_label.width()) * self.video_width) - y = int((end_pos.y() / self.image_label.height()) * self.video_height) - return x, y - - def thumbnail(self, image): - height, width, channel = image.shape - bytesPerLine = channel * width - qImg = QImage(image.data.tobytes(), width, height, bytesPerLine, QImage.Format_RGB888) - return qImg - - def lbl_mousePressEvent(self, event): - """마우스 클릭시 발생하는 이벤트, QLabel method overriding""" - - # 좌 클릭시 실행 - if event.buttons() == Qt.LeftButton: - self.isDrawing = True - self.begin = event.pos() - self.end = event.pos() - self.upper_left = (int((self.begin.x() / self.image_label.width()) * self.video_width), - int((self.begin.y() / self.image_label.height()) * self.video_height)) - self.image_label.update() - - self.draw_flag = True - - # 우 클릭시 실행 - elif event.buttons() == Qt.RightButton: - self.isDrawing = False - if len(self.left_range) > 0: - del self.distance[-1] - del self.target_name[-1] - del self.left_range[-1] - del self.right_range[-1] - self.save_target(self.current_camera) - self.draw_flag = False - self.image_label.update() - - def lbl_mouseMoveEvent(self, event): - """마우스가 움직일 때 발생하는 이벤트, QLabel method overriding""" - if event.buttons() == Qt.LeftButton: - self.end = event.pos() - self.image_label.update() - self.isDrawing = True - - def lbl_mouseReleaseEvent(self, event): - """마우스 클릭이 떼질 때 발생하는 이벤트, QLabel method overriding""" - if self.draw_flag: - self.end = event.pos() - self.image_label.update() - self.lower_right = (int((self.end.x() / self.image_label.width()) * self.video_width), - int((self.end.y() / self.image_label.height()) * self.video_height)) - text, ok = QInputDialog.getText(self, '거리 입력', '거리(km)') - if ok: - self.left_range.append(self.upper_left) - self.right_range.append(self.lower_right) - self.distance.append(text) - # self.min_xy = self.minrgb(self.upper_left, self.lower_right) - self.target_name.append("target_" + str(len(self.left_range))) - self.save_target(self.current_camera) - self.isDrawing = False - self.end_drawing = True - - print(f'{text} km') - else: - self.isDrawing = False - self.image_label.update() - - def data_csv_path(self): - fName = QFileDialog.getExistingDirectory( - self, 'Select path to save data csv file', JS06Settings.get('data_csv_path')) - if fName: - self.data_csv_path_textBrowser.setPlainText(fName) - else: - pass - - def target_csv_path(self): - fName = QFileDialog.getExistingDirectory( - self, 'Select path to save target csv file', JS06Settings.get('target_csv_path')) - if fName: - self.target_csv_path_textBrowser.setPlainText(fName) - else: - pass - - def image_save_path(self): - fName = QFileDialog.getExistingDirectory( - self, 'Select path to save image file', JS06Settings.get('image_save_path')) - if fName: - self.image_save_path_textBrowser.setPlainText(fName) - else: - pass - - def save_vis(self): - - col = ['datetime', 'camera_direction', - 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', - 'prevailing_visibility'] - result = pd.DataFrame(col) - print(result) - # result['datetime'] = - # result['camera_direction'] = - # result['N'] = - # result['NE'] = - # result['E'] = - # result['SE'] = - # result['S'] = - # result['SW'] = - # result['W'] = - # result['NW'] = - # result['prevailing_visibility'] = - # result.to_csv(f'{JS06Settings.get("data_csv_path")}/{self.current_camera}/{self.current_camera}.csv', - # index=False) - - def save_target(self, camera: str): - - print(f'타겟을 저장합니다 - {camera}') - if self.left_range: - col = ['target_name', 'left_range', 'right_range', 'distance'] - result = pd.DataFrame(columns=col) - result['target_name'] = self.target_name - result['left_range'] = self.left_range - result['right_range'] = self.right_range - result['distance'] = self.distance - result.to_csv(f'{JS06Settings.get("target_csv_path")}/{camera}/{camera}.csv', - mode='w', index=False) - print(f'{JS06Settings.get("target_csv_path")}/{camera}/{camera}.csv - SAVED!!!!') - - def get_target(self): - print("타겟을 불러옵니다.") - - save_path = os.path.join(f'{JS06Settings.get("target_csv_path")}/{self.current_camera}') - if os.path.isfile(f'{save_path}/{self.current_camera}.csv'): - target_df = pd.read_csv(f'{save_path}/{self.current_camera}.csv') - self.target_name = target_df["target_name"].tolist() - self.left_range = self.str_to_tuple(target_df["left_range"].tolist()) - self.right_range = self.str_to_tuple(target_df["right_range"].tolist()) - self.distance = target_df["distance"].tolist() - else: - QMessageBox.about(self, 'Error', 'no file...') - - def accept_click(self): - print('accept? yes accept~') - - # JS06Settings.set('data_csv_path', self.) - - -if __name__ == '__main__': - import sys - - app = QApplication(sys.argv) - ui = ND01SettingWidget() - ui.show() - sys.exit(app.exec_()) diff --git a/src/resources/auto_file_delete.ui b/src/resources/auto_file_delete.ui new file mode 100644 index 0000000..268f40c --- /dev/null +++ b/src/resources/auto_file_delete.ui @@ -0,0 +1,111 @@ + + + Form + + + Qt::WindowModal + + + + 0 + 0 + 298 + 282 + + + + + KoPubWorld돋움체 Medium + 10 + 50 + false + false + + + + ArrowCursor + + + File delete + + + This is this. + + + + + + + + + + + + 0 + 0 + + + + + Noto Sans + + + + Select a date and erase the data before that date + + + background-color: rgb(255, 255, 255); +color: rgb(0, 0, 0); + + + + + + Qt::Sunday + + + true + + + + + + + + 0 + 0 + + + + + Noto Sans + 10 + 50 + false + false + + + + PointingHandCursor + + + Exit this window + + + + + + Close + + + Ctrl+W + + + + + + + + + + diff --git a/src/resources/azimuth_off.png b/src/resources/azimuth_off.png new file mode 100644 index 0000000..3088db3 Binary files /dev/null and b/src/resources/azimuth_off.png differ diff --git a/src/resources/azimuth_on.png b/src/resources/azimuth_on.png new file mode 100644 index 0000000..05f3f91 Binary files /dev/null and b/src/resources/azimuth_on.png differ diff --git a/src/resources/asset/clock.png b/src/resources/clock.png similarity index 100% rename from src/resources/asset/clock.png rename to src/resources/clock.png diff --git a/src/resources/f_logo.png b/src/resources/f_logo.png new file mode 100644 index 0000000..369a90e Binary files /dev/null and b/src/resources/f_logo.png differ diff --git a/src/resources/asset/flip_off.png b/src/resources/flip_off.png similarity index 100% rename from src/resources/asset/flip_off.png rename to src/resources/flip_off.png diff --git a/src/resources/asset/flip_on.png b/src/resources/flip_on.png similarity index 100% rename from src/resources/asset/flip_on.png rename to src/resources/flip_on.png diff --git a/src/resources/asset/graph.png b/src/resources/graph.png similarity index 100% rename from src/resources/asset/graph.png rename to src/resources/graph.png diff --git a/src/resources/graph_range_window.ui b/src/resources/graph_range_window.ui new file mode 100644 index 0000000..060988d --- /dev/null +++ b/src/resources/graph_range_window.ui @@ -0,0 +1,80 @@ + + + Dialog + + + + 0 + 0 + 260 + 139 + + + + Dialog + + + + + 50 + 100 + 156 + 23 + + + + + Noto Sans + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 20 + 20 + 121 + 16 + + + + + 0 + 0 + + + + + Noto Sans + 8 + + + + Graph Time Range + + + + + + 20 + 40 + 221 + 31 + + + + + Noto Sans + + + + ex) 24 = Visibility values for 24 hours + + + + + + diff --git a/src/resources/asset/green.png b/src/resources/green.png similarity index 100% rename from src/resources/asset/green.png rename to src/resources/green.png diff --git a/src/resources/login_window.ui b/src/resources/login_window.ui new file mode 100644 index 0000000..ed1a42c --- /dev/null +++ b/src/resources/login_window.ui @@ -0,0 +1,138 @@ + + + Dialog + + + + 0 + 0 + 290 + 300 + + + + Login + + + background-color:rgb(22,32,42); + + + + + + + Noto Sans + + + + background-color:rgb(27,49,70); + + + + + + + Noto Sans + + + + background-color: #ffffff; + + + ID + + + + + + + + + + + + + asset/f_logo.png + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + Noto Sans + 10 + + + + color:#ff0000; + + + QFrame::Plain + + + + + + Qt::AutoText + + + Qt::AlignCenter + + + + + + + + Noto Sans + + + + background-color: #ffffff; + + + QLineEdit::PasswordEchoOnEdit + + + Password + + + + + + + + + + + Noto Sans + 75 + true + + + + background-color:rgb(27,49,70);color:#ffffff; + + + Login + + + + + + + + diff --git a/src/resources/asset/logo.png b/src/resources/logo.png similarity index 100% rename from src/resources/asset/logo.png rename to src/resources/logo.png diff --git a/src/resources/main_window.ui b/src/resources/main_window.ui index ca94d06..c062bd7 100644 --- a/src/resources/main_window.ui +++ b/src/resources/main_window.ui @@ -77,6 +77,9 @@ + + 0 + 0 @@ -208,7 +211,7 @@ background-color: #1b3146; - + true @@ -224,19 +227,21 @@ background-color: #1b3146; 55 + + + Noto Sans + + OpenHandCursor border:0px; -background-color: #1b3146; +background-color: #1b3146; +color: rgb(255, 255, 255); - - - - - asset/settings.pngasset/settings.png + Azimuth @@ -255,43 +260,23 @@ background-color: #1b3146; - - - - - - 0 - - - - - - - - - 6 - - - - - 0 - - - - - 0 - 0 - + + + + 0 + 0 + - + - 16777215 - 131 + 16 + 55 - background-color: rgb(255, 255, 255); + border:0px; +background-color: #1b3146; @@ -299,172 +284,144 @@ background-color: #1b3146; - + + + true + - + 0 0 - - - Noto Sans - 75 - true - - - - background-color:rgb(27,49,70); -color:#ffffff; - - - - - - Qt::AlignCenter - - - - - - - - - 0 - - - 0 - 0 + 55 - - - 16777215 - 131 - + + OpenHandCursor - background-color: rgb(255, 255, 255); + border:0px; +background-color: #1b3146; - - - - - - - 0 - 0 - + + + settings.pngsettings.png - - - Noto Sans - 75 - true - + + + 40 + 40 + - - background-color:rgb(27,49,70); -color:#ffffff; + + true - - + + true - - Qt::AlignCenter + + true - + 0 - - - - - 0 - 0 - - - - - 16777215 - 131 - - - - background-color: rgb(255, 255, 255); - - - - - - - - - - - 0 - 0 - - - - - Noto Sans - 75 - true - - - - background-color:rgb(27,49,70); -color:#ffffff; - - - - - - Qt::AlignCenter - - - + + 0 + + + 0 + + + + + + + 6 + - + 0 - - - - 0 - 0 - - - - - 16777215 - 131 - - - - background-color: rgb(255, 255, 255); - - - + + + 0 - + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + @@ -477,6 +434,7 @@ color:#ffffff; Noto Sans + 11 75 true @@ -496,31 +454,74 @@ color:#ffffff; - + 0 - - - - 0 - 0 - - - - - 16777215 - 131 - - - - background-color: rgb(255, 255, 255); - - - + + + 0 - + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + @@ -533,6 +534,7 @@ color:#ffffff; Noto Sans + 11 75 true @@ -552,31 +554,74 @@ color:#ffffff; - + 0 - - - - 0 - 0 - - - - - 16777215 - 131 - - - - background-color: rgb(255, 255, 255); - - - + + + 0 - + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 131 + + + + + + + + + + + + + @@ -589,6 +634,7 @@ color:#ffffff; Noto Sans + 11 75 true @@ -617,111 +663,182 @@ color:#ffffff; - + QLayout::SetMinimumSize - - - QLayout::SetMinimumSize - - - 12 + + + 6 - + 0 - - - - 0 - 0 - - - - - Noto Sans - 23 - + + + 0 - - background-color:rgb(27,49,70); + + + + + 0 + 0 + + + + + Noto Sans + 23 + + + + background-color:rgb(27,49,70); color: #ffffff; - - - Time series - - + + + Time series + + + + + + + + 0 + 0 + + + + + 0 + 42 + + + + border:0px; +background-color: #1b3146; + + + + + + + graph.pnggraph.png + + + + 32 + 32 + + + + true + + + true + + + true + + + + - - - - 0 - 0 - - - - - 0 - 42 - - - - border:0px; -background-color: #1b3146; - - - - - - - resources/icon/graph.pngresources/icon/graph.png - - - - 32 - 32 - - - - true - - - true - - - true - - + - + + + 0 + + + QLayout::SetMinimumSize + + + 6 + - - - - 0 - 0 - + + + 0 - + + + + + 0 + 0 + + + + + Noto Sans + 23 + + + + background-color:rgb(27,49,70); +color: #ffffff; + + + Discernment + + + + + + + + 0 + 0 + + + + + 0 + 42 + + + + border:0px; +background-color: #1b3146; + + + + + + + polar.pngpolar.png + + + + 35 + 35 + + + + true + + + true + + + true + + + + - - - - 0 - 0 - - - + @@ -786,7 +903,7 @@ background-color: #1b3146; - resources/icon/clock.pngresources/icon/clock.png + clock.pngclock.png @@ -877,7 +994,7 @@ background-color: #1b3146; - resources/icon/vis.pngresources/icon/vis.png + vis.pngvis.png @@ -904,7 +1021,7 @@ background-color: #1b3146; - color:rgb(28,136,227); + color:#ffffff; - km @@ -934,8 +1051,7 @@ background-color: #1b3146; - background-color:rgb(27,49,70); -color: #ffffff; + color: rgb(69, 165, 255); Prediction Visibility @@ -943,7 +1059,7 @@ color: #ffffff; - + 0 @@ -957,15 +1073,14 @@ color: #ffffff; - border:0px; -background-color: #1b3146; + color: rgb(69, 165, 255); - resources/icon/vis.pngresources/icon/vis.png + pre_vis_1.pngpre_vis_1.png @@ -992,7 +1107,7 @@ background-color: #1b3146; - color:#ffffff + color: rgb(69, 165, 255); - km diff --git a/src/resources/polar.png b/src/resources/polar.png new file mode 100644 index 0000000..ef675fe Binary files /dev/null and b/src/resources/polar.png differ diff --git a/src/resources/pre_vis.png b/src/resources/pre_vis.png new file mode 100644 index 0000000..502e033 Binary files /dev/null and b/src/resources/pre_vis.png differ diff --git a/src/resources/pre_vis_1.png b/src/resources/pre_vis_1.png new file mode 100644 index 0000000..c25686d Binary files /dev/null and b/src/resources/pre_vis_1.png differ diff --git a/src/resources/asset/red.png b/src/resources/red.png similarity index 100% rename from src/resources/asset/red.png rename to src/resources/red.png diff --git a/src/resources/settings.ui b/src/resources/setting_window.ui similarity index 78% rename from src/resources/settings.ui rename to src/resources/setting_window.ui index cd2102e..700fe31 100644 --- a/src/resources/settings.ui +++ b/src/resources/setting_window.ui @@ -6,8 +6,8 @@ 0 0 - 1159 - 888 + 971 + 558 @@ -95,6 +95,9 @@ color: #ffffff; 15 + + PointingHandCursor + border:0px; background-color: #1b3146; @@ -105,7 +108,7 @@ color: #ffffff; - asset/flip_off.pngasset/flip_off.png + flip_off.pngflip_off.png @@ -177,19 +180,13 @@ color: #ffffff; - - + + - 0 - 250 + 700 + 500 - - background-color:rgb(25,39,52); - - - - @@ -222,22 +219,6 @@ color: #ffffff; - - - - - 0 - 250 - - - - background-color:rgb(25,39,52); - - - - - - @@ -273,7 +254,7 @@ color: #ffffff; - + 0 0 @@ -285,7 +266,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -345,7 +326,7 @@ color: #ffffff; - + 0 0 @@ -357,7 +338,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -417,7 +398,7 @@ color: #ffffff; - + 0 0 @@ -429,7 +410,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -482,14 +463,107 @@ color: #ffffff; - + 0 + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + Noto Sans + 10 + + + + background-color:rgb(27,49,70); +color: #ffffff; + + + Graph setting: + + + + + + + + Noto Sans + 50 + false + + + + color: rgb(255, 255, 255); + + + Red + + + true + + + + + + + + Noto Sans + 50 + false + + + + color: rgb(255, 255, 255); + + + Green + + + true + + + + + + + + Noto Sans + 50 + false + + + + color: rgb(255, 255, 255); + + + Blue + + + true + + + + + + + + + 6 + - + 0 0 @@ -501,7 +575,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -534,12 +608,12 @@ color: #ffffff; - 0 + 6 - + 0 0 @@ -551,7 +625,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -592,12 +666,12 @@ color: #ffffff; - 0 + 6 - + 0 0 @@ -609,7 +683,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -635,12 +709,12 @@ color: #ffffff; - 0 + 6 - + 0 0 @@ -652,7 +726,7 @@ color: #ffffff; - + background-color:rgb(27,49,70); color: #ffffff; @@ -676,55 +750,57 @@ color: #ffffff; - - - - Noto Sans - 10 - - - - background-color:rgb(27,49,70); -color: #ffffff; - - - Other QSetting 1 - - - - - - - - Noto Sans - 10 - + + + 6 - - background-color:rgb(27,49,70); + + + + + 0 + 0 + + + + + Noto Sans + 10 + + + + background-color:rgb(27,49,70); color: #ffffff; - - - Other QSetting 2 - - - - - - - - Noto Sans - 10 - - - - background-color:rgb(27,49,70); + + + File delete: + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + background-color:rgb(27,49,70); color: #ffffff; - - - Other QSetting 3 - - + + + + + + + @@ -750,6 +826,9 @@ color: #ffffff; QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + false + diff --git a/src/resources/asset/settings.png b/src/resources/settings.png similarity index 100% rename from src/resources/asset/settings.png rename to src/resources/settings.png diff --git a/src/resources/asset/settings_on.png b/src/resources/settings_on.png similarity index 100% rename from src/resources/asset/settings_on.png rename to src/resources/settings_on.png diff --git a/src/resources/thumb.ui b/src/resources/thumb.ui new file mode 100644 index 0000000..b43fb07 --- /dev/null +++ b/src/resources/thumb.ui @@ -0,0 +1,341 @@ + + + MainWindow + + + true + + + + 0 + 0 + 784 + 366 + + + + + 0 + 0 + + + + + 400 + 100 + + + + + Noto Sans + 9 + 50 + false + false + + + + false + + + JS-08 + + + background-color:rgb(22,32,42); +border-color: rgb(255, 255, 255); + + + Qt::ToolButtonIconOnly + + + QTabWidget::Rounded + + + true + + + + + 16777215 + 16777215 + + + + false + + + + + + + 0 + + + 2 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 2 + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + + Noto Sans + 15 + + + + background-color:rgb(27,49,70); +color: rgb(255, 255, 255); + + + Front + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + Noto Sans + 15 + + + + background-color:rgb(27,49,70); +color: rgb(255, 255, 255); + + + Rear + + + Qt::AlignCenter + + + + + + + + + 6 + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 30 + + + + + + + <html><head/><body><p><br/></p></body></html> + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 30 + + + + + + + <html><head/><body><p><br/></p></body></html> + + + Qt::AlignCenter + + + + + + + + + + + + &Quit + + + Exit + + + Exit JS-06 + + + Ctrl+W + + + + + sd + + + + + true + + + true + + + km + + + + + true + + + true + + + mi + + + + + true + + + false + + + Inference + + + I + + + + + false + + + Edit &Target + + + + + true + + + Edit &Camera Info + + + + + About + + + + + Con&figuration + + + + + + + + + actionExit + triggered() + MainWindow + close() + + + -1 + -1 + + + 187 + 228 + + + + + diff --git a/src/resources/thumbnail_view.ui b/src/resources/thumbnail_view.ui index f8c1849..fa8ac0b 100644 --- a/src/resources/thumbnail_view.ui +++ b/src/resources/thumbnail_view.ui @@ -85,27 +85,27 @@ border-color: rgb(255, 255, 255); - 0 + 2 - 5 + 0 - 5 + 2 - 5 + 0 - 5 + 0 - 6 + 2 - QLayout::SetMinimumSize + QLayout::SetDefaultConstraint @@ -118,7 +118,9 @@ border-color: rgb(255, 255, 255); Noto Sans - 15 + 17 + 50 + false @@ -144,7 +146,9 @@ color: rgb(255, 255, 255); Noto Sans - 15 + 17 + 50 + false @@ -164,32 +168,27 @@ color: rgb(255, 255, 255); - 6 + 2 - + 0 0 - - - 952 - 362 - - - 30 + Noto Sans + 20 color: rgb(255, 255, 255); - <html><head/><body><p><br/></p></body></html> + No file Qt::AlignCenter @@ -199,27 +198,22 @@ color: rgb(255, 255, 255); - + 0 0 - - - 952 - 362 - - - 30 + Noto Sans + 20 color: rgb(255, 255, 255); - <html><head/><body><p><br/></p></body></html> + No file Qt::AlignCenter @@ -315,7 +309,7 @@ color: rgb(255, 255, 255); - + diff --git a/src/resources/asset/vis.png b/src/resources/vis.png similarity index 100% rename from src/resources/asset/vis.png rename to src/resources/vis.png diff --git a/src/target_info.py b/src/target_info.py new file mode 100644 index 0000000..ce95a80 --- /dev/null +++ b/src/target_info.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +import os +import pandas as pd +import numpy as np + +import cal_ext_coef +from model import JS08Settings + + +def minprint(epoch, left_range, right_range, distance, cv_img, camera): + """A function that outputs pixels for calculating the dissipation coefficient in the specified areas""" + + cp_image = cv_img.copy() + min_x = [] + min_y = [] + + for upper_left, lower_right in zip(left_range, right_range): + result = minrgb(upper_left, lower_right, cp_image) + min_x.append(result[0]) + min_y.append(result[1]) + + visibility = get_rgb(epoch, min_x, min_y, cp_image, distance, camera) + + # print(f'minprint visibility: {visibility}') + return visibility + + +def minrgb(upper_left, lower_right, cp_image): + """Extracts the minimum RGB value of the dragged area""" + + up_y = min(upper_left[1], lower_right[1]) + down_y = max(upper_left[1], lower_right[1]) + + left_x = min(upper_left[0], lower_right[0]) + right_x = max(upper_left[0], lower_right[0]) + + target = cp_image[up_y:down_y, left_x:right_x, :] + + r = target[:, :, 0] + g = target[:, :, 1] + b = target[:, :, 2] + + r = np.clip(r, 0, 765) + sum_rgb = r + g + b + + t_idx = np.where(sum_rgb == np.min(sum_rgb)) + + show_min_y = t_idx[0][0] + up_y + show_min_x = t_idx[1][0] + left_x + + return show_min_x, show_min_y + + +def get_rgb(epoch: str, min_x, min_y, cp_image, distance, camera): + """Gets the RGB values of the coordinates.""" + + r_list = [] + g_list = [] + b_list = [] + + for x, y in zip(min_x, min_y): + r_list.append(cp_image[y, x, 0]) + g_list.append(cp_image[y, x, 1]) + b_list.append(cp_image[y, x, 2]) + + visibility = save_rgb(r_list, g_list, b_list, epoch, distance, camera) + + # print(f'get_rgb visibility: {visibility}') + return visibility + + +def save_rgb(r_list, g_list, b_list, epoch, distance, camera): + """Save the rgb information for each target.""" + + save_path = os.path.join(f'rgb/{camera}') + os.makedirs(save_path, exist_ok=True) + + try: + if r_list: + r_list = list(map(int, r_list)) + g_list = list(map(int, g_list)) + b_list = list(map(int, b_list)) + + col = ['target_name', 'r', 'g', 'b', 'distance'] + result = pd.DataFrame(columns=col) + result['target_name'] = [f'target_{num}' for num in range(1, len(r_list) + 1)] + result['r'] = r_list + result['g'] = g_list + result['b'] = b_list + result['distance'] = distance + result.to_csv(f'{save_path}/{epoch}.csv', mode='w', index=False) + list1, list2, list3, select_color = cal_ext_coef.cal_curve(result) + + # visibility = extinc_print(list1, list2, list3, select_color) + visibility = extinc_print(list3, select_color) + + # print(f'save_rgb visibility: {visibility}') + return visibility + + except TypeError: + pass + + +def extinc_print(alp_list: list, select_color: str = ""): + """Select an appropriate value among visibility by wavelength.""" + visibility = 0 + + if select_color == 'red': + visibility = visibility_print(alp_list[0]) + elif select_color == 'green': + visibility = visibility_print(alp_list[1]) + elif select_color == 'blue': + visibility = visibility_print(alp_list[2]) + + # print(f'extinc_print visibility: {visibility}') + return visibility + + +def visibility_print(ext_g: float = 0.0): + """Print the visibility""" + + vis_value = (3.912 / ext_g) + if vis_value > 20: + vis_value = 20 + elif vis_value <= 0.01: + vis_value = 0.01 + + vis_value_str = f'{vis_value:.3f}' + + return vis_value_str + + +def get_target(camera_name: str): + """Retrieves target information of a specific camera.""" + + save_path = JS08Settings.get('target_csv_path') + + if os.path.isfile(f'{save_path}/{camera_name}/{camera_name}.csv'): + target_df = pd.read_csv(f'{save_path}/{camera_name}/{camera_name}.csv') + target_name = target_df['target_name'].tolist() + left_range = str_to_tuple(target_df['left_range'].tolist()) + right_range = str_to_tuple(target_df['right_range'].tolist()) + distance = target_df['distance'].tolist() + azimuth = target_df['azimuth'].tolist() + + return target_name, left_range, right_range, distance, azimuth + + else: + return [], [], [], [], [] + + +def get_target_from_azimuth(camera_name: str, azimuth: str): + """Retrieves target information from azimuth of a specific camera""" + + global target_name, left_range, right_range, distance + save_path = JS08Settings.get('target_csv_path') + + if os.path.isfile(f'{save_path}/{camera_name}/{camera_name}.csv'): + target_df = pd.read_csv(f'{save_path}/{camera_name}/{camera_name}.csv') + azi_target = target_df.loc[(target_df['azimuth'] == f'{azimuth}'), :] + + target_name = azi_target['target_name'].tolist() + left_range = str_to_tuple(azi_target['left_range'].tolist()) + right_range = str_to_tuple(azi_target['right_range'].tolist()) + distance = azi_target['distance'].tolist() + + return target_name, left_range, right_range, distance, azimuth + + else: + + + return [], [], [], [], [] + + # if select_data == 'target_name': + # return target_name + # + # elif select_data == 'left_range': + # return left_range + # + # elif select_data == 'right_range': + # return right_range + # + # elif select_data == 'distance': + # return distance + + +def str_to_tuple(before_list): + """A function that converts the tuple list, which is the location information of the stored targets, + into a string and converts it back into a tuple form.""" + tuple_list = [i.split(',') for i in before_list] + tuple_list = [(int(i[0][1:]), int(i[1][:-1])) for i in tuple_list] + return tuple_list + + +if __name__ == '__main__': + + front_target_name_W, front_left_range_W, front_right_range_W, front_distance_W, front_azimuth_W = \ + target_info.get_target_from_azimuth(front_cap_name, 'W') + + print(front_target_name_W) + print(front_left_range_W) + print(front_right_range_W) + print(front_distance_W) + print(front_azimuth_W) diff --git a/src/test.png b/src/test.png new file mode 100644 index 0000000..883cdae Binary files /dev/null and b/src/test.png differ diff --git a/src/video_thread_mp.py b/src/video_thread_mp.py index b58d47b..95f1557 100644 --- a/src/video_thread_mp.py +++ b/src/video_thread_mp.py @@ -1,224 +1,157 @@ -# !/usr/bin/env python3 +#!/usr/bin/env python3 +# +# Copyright 2021-2022 Sijung Co., Ltd. +# +# Authors: +# cotjdals5450@gmail.com (Seong Min Chae) +# 5jx2oh@gmail.com (Jongjin Oh) + import os + import cv2 import time -import numpy as np -import pandas as pd -import multiprocessing as mp -from multiprocessing import Process, Queue -from PyQt5.QtCore import QThread, pyqtSignal, QObject -import curve_save -from model import JS06Settings +from model import JS08Settings +import target_info def producer(q): - # proc = mp.current_process() - # print(f'{proc.name} multiprocessing start.') - - cap = cv2.VideoCapture("rtsp://admin:sijung5520@192.168.100.100/profile2/media.smp") - while True: - epoch = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time())) - date = epoch[2:6] - - if epoch[-2:] == "00": - try: - image_save_path = JS06Settings.get('image_save_path') - os.makedirs(f'{image_save_path}/vista/{date}', exist_ok=True) - os.makedirs(f'{image_save_path}/resize/{date}', exist_ok=True) - - _, frame = cap.read() - cv2.imwrite(f'{image_save_path}/vista/{date}/{epoch}.png', frame) - cv2.imwrite(f'{image_save_path}/resize/{date}/{epoch}.jpg', cv2.resize(frame, (315, 131))) - cv2.destroyAllWindows() - - # left_range, right_range, distance = get_target("PNM_9030V") - # visibility = minprint(epoch[:-2], left_range, right_range, distance, frame) - # - # q.put(visibility) - time.sleep(1) - except Exception as e: - print(e) - cap.release() - cap = cv2.VideoCapture("rtsp://admin:sijung5520@192.168.100.100/profile2/media.smp") - continue - - -def minprint(epoch, left_range, right_range, distance, cv_img): - """A function that outputs pixels for calculating the dissipation coefficient in the specified areas""" - print("minprint 시작") - # epoch = time.strftime("%Y%m%d%H%M", time.localtime(time.time())) - cp_image = cv_img.copy() - result = () - cnt = 1 - min_x = [] - min_y = [] - - for upper_left, lower_right in zip(left_range, right_range): - result = minrgb(upper_left, lower_right, cp_image) - min_x.append(result[0]) - min_y.append(result[1]) - cnt += 1 - - visibility = get_rgb(epoch, min_x, min_y, cp_image, distance) - return visibility - - -def minrgb(upper_left, lower_right, cp_image): - """Extracts the minimum RGB value of the dragged area""" - - up_y = min(upper_left[1], lower_right[1]) - down_y = max(upper_left[1], lower_right[1]) - - left_x = min(upper_left[0], lower_right[0]) - right_x = max(upper_left[0], lower_right[0]) - - test = cp_image[up_y:down_y, left_x:right_x, :] - - r = test[:, :, 0] - g = test[:, :, 1] - b = test[:, :, 2] - - r = np.clip(r, 0, 765) - sum_rgb = r + g + b - - t_idx = np.where(sum_rgb == np.min(sum_rgb)) + front_cap_name = 'PNM_9031RV_front' + rear_cap_name = 'PNM_9031RV_rear' + + front_cap = cv2.VideoCapture(JS08Settings.get('front_camera_rtsp')) + rear_cap = cv2.VideoCapture(JS08Settings.get('rear_camera_rtsp')) + + if rear_cap.isOpened() and front_cap.isOpened(): + while True: + epoch = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) + date = epoch[2:8] + + if epoch[-2:] == '00': + front_target_name, front_left_range, front_right_range, front_distance, front_azimuth = \ + target_info.get_target(front_cap_name) + + rear_target_name, rear_left_range, rear_right_range, rear_distance, rear_azimuth = \ + target_info.get_target(rear_cap_name) + + front_target_name_W, front_left_range_W, front_right_range_W, front_distance_W, front_azimuth_W = \ + target_info.get_target_from_azimuth(front_cap_name, 'W') + + front_target_name_NW, front_left_range_NW, front_right_range_NW, front_distance_NW, front_azimuth_NW = \ + target_info.get_target_from_azimuth(front_cap_name, 'NW') + + front_target_name_N, front_left_range_N, front_right_range_N, front_distance_N, front_azimuth_N = \ + target_info.get_target_from_azimuth(front_cap_name, 'N') + + front_target_name_NE, front_left_range_NE, front_right_range_NE, front_distance_NE, front_azimuth_NE = \ + target_info.get_target_from_azimuth(front_cap_name, 'NE') + + front_target_name_E, front_left_range_E, front_right_range_E, front_distance_E, front_azimuth_E = \ + target_info.get_target_from_azimuth(front_cap_name, 'E') + + rear_target_name_E, rear_left_range_E, rear_right_range_E, rear_distance_E, rear_azimuth_E = \ + target_info.get_target_from_azimuth(rear_cap_name, 'E') + + rear_target_name_SE, rear_left_range_SE, rear_right_range_SE, rear_distance_SE, rear_azimuth_SE = \ + target_info.get_target_from_azimuth(rear_cap_name, 'SE') + + rear_target_name_S, rear_left_range_S, rear_right_range_S, rear_distance_S, rear_azimuth_S = \ + target_info.get_target_from_azimuth(rear_cap_name, 'S') + + rear_target_name_SW, rear_left_range_SW, rear_right_range_SW, rear_distance_SW, rear_azimuth_SW = \ + target_info.get_target_from_azimuth(rear_cap_name, 'SW') + + rear_target_name_W, rear_left_range_W, rear_right_range_W, rear_distance_W, rear_azimuth_W = \ + target_info.get_target_from_azimuth(rear_cap_name, 'W') + + if len(front_left_range_W) < 4 and len(rear_left_range_W) < 4: + continue + else: + pass + + image_save_path = JS08Settings.get('image_save_path') + os.makedirs(f'{image_save_path}/vista/{front_cap_name}/{date}', exist_ok=True) + os.makedirs(f'{image_save_path}/vista/{rear_cap_name}/{date}', exist_ok=True) + os.makedirs(f'{image_save_path}/resize/{front_cap_name}/{date}', exist_ok=True) + os.makedirs(f'{image_save_path}/resize/{rear_cap_name}/{date}', exist_ok=True) + + front_ret, front_frame = front_cap.read() + rear_ret, rear_frame = rear_cap.read() + + if not front_ret or not rear_ret: + print('Found Error; Rebuilding stream') + + front_cap.release() + rear_cap.release() + front_cap = cv2.VideoCapture(JS08Settings.get('front_camera_rtsp')) + rear_cap = cv2.VideoCapture(JS08Settings.get('rear_camera_rtsp')) + front_ret, front_frame = front_cap.read() + rear_ret, rear_frame = rear_cap.read() + + visibility_front = target_info.minprint(epoch[:-2], front_left_range, front_right_range, + front_distance, front_frame, front_cap_name) + visibility_rear = target_info.minprint(epoch[:-2], rear_left_range, rear_right_range, + rear_distance, rear_frame, rear_cap_name) + + visibility_front_W = target_info.minprint(epoch[:-2], front_left_range_W, front_right_range_W, + front_distance_W, front_frame, front_cap_name) + visibility_front_NW = target_info.minprint(epoch[:-2], front_left_range_NW, front_right_range_NW, + front_distance_NW, front_frame, front_cap_name) + visibility_front_N = target_info.minprint(epoch[:-2], front_left_range_N, front_right_range_N, + front_distance_N, front_frame, front_cap_name) + visibility_front_NE = target_info.minprint(epoch[:-2], front_left_range_NE, front_right_range_NE, + front_distance_NE, front_frame, front_cap_name) + visibility_front_E = target_info.minprint(epoch[:-2], front_left_range_E, front_right_range_E, + front_distance_E, front_frame, front_cap_name) + + visibility_rear_E = target_info.minprint(epoch[:-2], rear_left_range_E, rear_right_range_E, + rear_distance_E, rear_frame, rear_cap_name) + visibility_rear_SE = target_info.minprint(epoch[:-2], rear_left_range_SE, rear_right_range_SE, + rear_distance_SE, rear_frame, rear_cap_name) + visibility_rear_S = target_info.minprint(epoch[:-2], rear_left_range_S, rear_right_range_S, + rear_distance_S, rear_frame, rear_cap_name) + visibility_rear_SW = target_info.minprint(epoch[:-2], rear_left_range_SW, rear_right_range_SW, + rear_distance_SW, rear_frame, rear_cap_name) + visibility_rear_W = target_info.minprint(epoch[:-2], rear_left_range_W, rear_right_range_W, + rear_distance_W, rear_frame, rear_cap_name) + + # print(f'visibility W, NW, N, NE, E - {visibility_front_W, visibility_front_NW, visibility_front_N, visibility_front_NE, visibility_front_E}') + # print(f'visibility E, SE, S, SW, W - {visibility_rear_E, visibility_rear_SE, visibility_rear_S, visibility_rear_SW, visibility_rear_W}') + + visibility = {'visibility_front': visibility_front, 'visibility_rear': visibility_rear, + 'front_W': visibility_front_W, 'front_NW': visibility_front_NW, + 'front_N': visibility_front_N, 'front_NE': visibility_front_NE, + 'front_E': visibility_front_E, 'rear_E': visibility_rear_E, + 'rear_SE': visibility_rear_SE, 'rear_S': visibility_rear_S, + 'rear_SW': visibility_rear_SW, 'rear_W': visibility_rear_W} + q.put(visibility) + + if JS08Settings.get('image_size') == 0: # Original size + cv2.imwrite(f'{image_save_path}/vista/{front_cap_name}/{date}/{epoch}.png', front_frame) + cv2.imwrite(f'{image_save_path}/vista/{rear_cap_name}/{date}/{epoch}.png', rear_frame) + + elif JS08Settings.get('image_size') == 1: # FHD size + front_frame = cv2.resize(front_frame, (1920, 640), interpolation=cv2.INTER_LINEAR) + rear_frame = cv2.resize(rear_frame, (1920, 640), interpolation=cv2.INTER_LINEAR) + + cv2.imwrite( + f'{image_save_path}/vista/{front_cap_name}/{date}/{epoch}_{front_cap_name}.png', front_frame) + cv2.imwrite( + f'{image_save_path}/vista/{rear_cap_name}/{date}/{epoch}_{rear_cap_name}.png', rear_frame) + + front_frame = cv2.resize(front_frame, (393, 105), interpolation=cv2.INTER_NEAREST) # Thumbnail size + rear_frame = cv2.resize(rear_frame, (393, 105), interpolation=cv2.INTER_LINEAR) + cv2.imwrite( + f'{image_save_path}/resize/{front_cap_name}/{date}/{epoch}.jpg', front_frame) + cv2.imwrite( + f'{image_save_path}/resize/{rear_cap_name}/{date}/{epoch}.jpg', rear_frame) - print("red : ", cp_image[t_idx[0][0] + up_y, t_idx[1][0] + left_x, 0]) - print("green : ", cp_image[t_idx[0][0] + up_y, t_idx[1][0] + left_x, 1]) - print("blue : ", cp_image[t_idx[0][0] + up_y, t_idx[1][0] + left_x, 2]) - show_min_y = t_idx[0][0] + up_y - show_min_x = t_idx[1][0] + left_x - - return (show_min_x, show_min_y) - - -def get_rgb(epoch: str, min_x, min_y, cp_image, distance): - """Gets the RGB values ​​of the coordinates.""" - r_list = [] - g_list = [] - b_list = [] - - for x, y in zip(min_x, min_y): - r_list.append(cp_image[y, x, 0]) - g_list.append(cp_image[y, x, 1]) - b_list.append(cp_image[y, x, 2]) - - print("red list : ", r_list) - print("green list : ", g_list) - print("blue list : ", b_list) - - visibility = save_rgb(r_list, g_list, b_list, epoch, distance) - return visibility - - -def save_rgb(r_list, g_list, b_list, epoch, distance): - """Save the rgb information for each target.""" - try: - save_path = os.path.join(f"rgb/PNM_9030V") - os.mkdir(save_path) - - except Exception as e: - pass - - if r_list: - r_list = list(map(int, r_list)) - g_list = list(map(int, g_list)) - b_list = list(map(int, b_list)) - - col = ["target_name", "r", "g", "b", "distance"] - result = pd.DataFrame(columns=col) - result["target_name"] = [f"target_{num}" for num in range(1, len(r_list) + 1)] - result["r"] = r_list - result["g"] = g_list - result["b"] = b_list - result["distance"] = distance - result.to_csv(f"{save_path}/{epoch}.csv", mode="w", index=False) - list1, list2, list3, select_color = curve_save.cal_curve(result) - visibility = extinc_print(list1, list2, list3, select_color) - print(result) - print("Save rgb") - - return visibility - - -def extinc_print(c1_list: list = [0, 0, 0], c2_list: list = [0, 0, 0], alp_list: list = [0, 0, 0], - select_color: str = ""): - """Select an appropriate value among visibility by wavelength.""" - g_ext = round(alp_list[1], 1) + time.sleep(1) + front_cap.release() + rear_cap.release() + front_cap = cv2.VideoCapture(JS08Settings.get('front_camera_rtsp')) + rear_cap = cv2.VideoCapture(JS08Settings.get('rear_camera_rtsp')) - if select_color == "red": - visibility = visibility_print(alp_list[0]) - elif select_color == "green": - visibility = visibility_print(alp_list[1]) + cv2.destroyAllWindows() else: - visibility = visibility_print(alp_list[2]) - - return visibility - - -def visibility_print(ext_g: float = 0.0): - """Print the visibility""" - vis_value = 0 - - vis_value = (3.912 / ext_g) - if vis_value > 20: - vis_value = 20 - elif vis_value < 0.01: - vis_value = 0.01 - - # self.data_storage(vis_value) - vis_value_str = f"{vis_value:.2f}" + " km" - return vis_value_str - - -def get_target(camera_name: str): - """Retrieves target information of a specific camera.""" - - save_path = os.path.join(f"target/{camera_name}") - print("Get target information") - if os.path.isfile(f"{save_path}/{camera_name}.csv"): - target_df = pd.read_csv(f"{save_path}/{camera_name}.csv") - target_name = target_df["target_name"].tolist() - left_range = target_df["left_range"].tolist() - left_range = str_to_tuple(left_range) - right_range = target_df["right_range"].tolist() - right_range = str_to_tuple(right_range) - distance = target_df["distance"].tolist() - return left_range, right_range, distance - - -def str_to_tuple(before_list): - """A function that converts the tuple list, which is the location information of the stored targets, - into a string and converts it back into a tuple form.""" - tuple_list = [i.split(',') for i in before_list] - tuple_list = [(int(i[0][1:]), int(i[1][:-1])) for i in tuple_list] - return tuple_list - - -class VideoThread(QThread): - update_pixmap_signal = pyqtSignal(str) - - def __init__(self, src: str = "", file_type: str = "None", q: Queue = None): - super().__init__() - self._run_flag = False - self.src = src - self.file_type = file_type - self.q = q - - def run(self): - self._run_flag = True - ## 영상 입력이 카메라일 때 - if self.file_type == "Video": - print("비디오 쓰레드 시작") - while self._run_flag: - if not self.q.empty(): - cv_img = self.q.get() - self.update_pixmap_signal.emit(cv_img) - # shut down capture system - - def stop(self): - """Sets run flag to False and waits for thread to finish""" - self._run_flag = False - self.quit() - self.wait() + print('cap closed')