diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..3a718d5 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/DBManager.py b/DBManager.py deleted file mode 100644 index f2c689e..0000000 --- a/DBManager.py +++ /dev/null @@ -1,41 +0,0 @@ -import pymysql -import sqlite3 - -class DBManager(): - #实例化数据库管理对象,并连接数据库 - def __init__(self,ip,prot,user,passwd): - pass - - #检查设备ID是否在数据库? - def checkDevID(self,cID): - pass - - def test(self): - # 建立数据库连接 - conn = pymysql.connect( - host='localhost', - port=3306, - user='username', - password='password', - database='database_name' - ) - - # 创建游标对象 - cursor = conn.cursor() - - # 执行 SQL 查询 - query = "SELECT * FROM table_name" - cursor.execute(query) - - # 获取查询结果 - result = cursor.fetchall() - - # 输出结果 - for row in result: - print(row) - - # 关闭游标和连接 - cursor.close() - conn.close() - - diff --git a/config.yaml b/config.yaml index 91bb54e..90e30bd 100644 --- a/config.yaml +++ b/config.yaml @@ -1,27 +1,34 @@ -#字典更新接口 -url: http://192.168.2.61/api/ -hyMD5: http://192.168.2.61/api/defaultMd5/download -hyfile: http://192.168.2.61/api/defaultPassword/download/ -hyget: http://192.168.2.61/api/defaultPassword/export/ -#主线程数 -concurrency: 6 #同时执行的子任务数,--线程数 -#超时配置 -TagTime: 60 #任务执行超时时间 -pocTime: 30 #一次poc执行超时时间,0不进行超时判断 -nmapTime: 10 #一次nmap执行超时时间,0不进行超时判断 #日志记录 file_log_level: INFO #是否记录日志 show_log_level: DEBUG #日志记录级别 log_dir: logs -#远程数据库 + +#远程数据库 --- 香橙派用不了 +DBType: 1 #0--mysql,1--sqlite mysql: - host: 192.168.2.54 + host: 192.168.3.45 port: 3306 user: root - pass: crnn@cz*** - db: sfind + pass: ZFkj_123456 + db: zfbox + +#sqlit +sqlite: zfbox.db +#用户初始密码 +pw: zfkj_123!@# +#上传限制 +MAX_CONTENT_LENGTH : 100 # 100MB +UPLOAD_FOLDER : uploads +ALLOWED_EXTENSIONS : {'zip'} +#RTSP +RTSP_Check_Time : 600 #10分钟 +#model +weight_path: /model/weights +yolov5_path: model/base_model/yolov5 +cap_sleep_time: 300 #5分钟 +buffer_len: 30 #分析后画面缓冲区帧数 diff --git a/core/DBManager.py b/core/DBManager.py new file mode 100644 index 0000000..55e4bef --- /dev/null +++ b/core/DBManager.py @@ -0,0 +1,218 @@ +import pymysql +import sqlite3 +import threading +import os +from myutils.ConfigManager import myCongif +from myutils.MyLogger_logger import LogHandler + +class DBManager(): + #实例化数据库管理对象,并连接数据库 + #itype=0 使用mysql数据库,1-使用sqlite数据库 + def __init__(self): + self.logger = LogHandler().get_logger("DBManager") + self.lock = threading.Lock() + self.itype = myCongif.get_data("DBType") + if self.itype ==0: + self.host = myCongif.get_data('mysql.host') + self.port = myCongif.get_data('mysql.port') + self.user = myCongif.get_data('mysql.user') + self.passwd = myCongif.get_data('mysql.passwd') + self.database = myCongif.get_data('mysql.database') + self.connection = None + self.cursor = None + elif self.itype ==1: + self.dbfile = myCongif.get_data("sqlite") + if not os.path.exists(self.dbfile): + self.dbfile = "../" + self.dbfile #直接运行DBManager时初始路径不是在根目录 + if not os.path.exists(self.dbfile): + raise FileNotFoundError(f"Database file {self.dbfile} does not exist.") + else: + self.logger.error("错误的数据库类型,请检查") + + + def __del__(self): + if self.ok: + self.cursor.close() + self.connection.close() + self.cursor = None + self.connection = None + self.logger.debug("DBManager销毁") + + def connect(self): + try: + if self.itype ==0: + self.connection = pymysql.connect(host=self.host, port=self.port, user=self.user, + passwd=self.passwd, db=self.database,charset='utf8') + self.cursor = self.connection.cursor() + elif self.itype ==1: + self.connection = sqlite3.connect(self.dbfile) + self.cursor = self.connection.cursor() + self.ok = True + return True + except: + self.logger.error("服务器端数据库连接失败") + return False + + # 判断数据库连接是否正常,若不正常则重连接 + def Retest_conn(self): + if self.itype == 0: #除了mysql,sqlite3不需要判断连接状态 + try: + self.connection.ping() + except: + return self.connect() + return True + + # 执行数据库查询操作 1-只查询一条记录,其他所有记录 + def do_select(self, strsql, itype=0): + # self.conn.begin() + self.lock.acquire() + data = None + if self.Retest_conn(): + try: + self.cursor.execute(strsql) + self.connection.commit() # select要commit提交事务,是存在获取不到最新数据的问题(innoDB事务机制) + except Exception as e: + self.logger.error("do_select异常报错:%s" % str(e)) + self.lock.release() + return None + if itype == 1: + data = self.cursor.fetchone() + else: + data = self.cursor.fetchall() + self.lock.release() + return data + + # 执行数据库语句 + def do_sql(self, strsql, data=None): + self.lock.acquire() + bok = False + if self.Retest_conn(): + try: + # self.conn.begin() + if data: + iret = self.cursor.executemany(strsql, data) #批量执行sql语句 + else: + iret = self.cursor.execute(strsql) + self.connection.commit() + if iret.rowcount > 0: # 要有修改成功的记录才返回true + bok = True + except Exception as e: + self.logger.error("执行数据库语句%s出错:%s" % (strsql, str(e))) + self.connection.rollback() + self.lock.release() + return bok + + #---------------------特定数据库操作函数--------------------- + #根据通道ID或者模型ID删除通道和模型间的关联数据 1-通道ID,2-模型ID + def delC2M(self,ID,itype): + #channel2model + if itype ==1: + strsql = f"select ID from channel2model where channel_id={ID};" + datas = self.do_select(strsql) + + strsql = f"delete from channel2model where channel_id={ID};" + ret = self.do_sql(strsql) + if ret == False: + return False + elif itype ==2: + strsql = f"select ID from channel2model where model_id={ID};" + datas = self.do_select(strsql) + + strsql = f"delete from channel2model where model_id={ID};" + ret = self.do_sql(strsql) + if ret == False: + return False + else: + return False + #schedule + for data in datas: + c2m_id = data[0] + strsql = f"delete from schedule where channel2model_id={c2m_id};" + ret = self.do_sql(strsql) + if ret == False: + return False + return True + + #删除通道,需要关联删除布防时间,通道和算法的关联表 + def delchannel(self,ID): + ret = self.delC2M(ID) + if ret == False: + return False + #channel + strsql = f"delete from channel where ID={ID};" + ret = self.do_sql(strsql) + if ret == False: + return False + return True + + #修改视频通道和算法间的关联关系 + #channel_id 通道ID + #modell_list 最新配置的模型id list + def updateC2M(self,channel_id,model_list): + strsql = f"select model_id from channel2model where channel_id={channel_id};" + datas = set(self.do_select(strsql)) + data_new = set(model_list) + #计算要新增和修改的 + need_add = data_new - datas + need_del = datas-data_new + #新增 + for one in need_add: + strsql = f"insert into channel2model (channel_id,model_id) values ({channel_id},{one});" + if self.do_sql(strsql) == False: + return False + #差删除 + for one in need_del: + strsql = f"select ID from channel2model where channel_id={channel_id} and model_id={one};" + c2m_id = mDBM.do_select(strsql,1)[0] + #删除布防计划数据 + strsql = f"delete from schedule where channel2model_id={c2m_id};" + if self.do_sql(strsql) == False: + return False + #删除关联记录 + strsql = f"delete from channel2model where ID = {c2m_id};" + if self.do_sql(strsql) == False: + return False + return True + + #检查设备ID是否在数据库? + def checkDevID(self,cID): + pass + + def test(self): + # 建立数据库连接 + conn = pymysql.connect( + host='localhost', + port=3306, + user='username', + password='password', + database='database_name' + ) + + # 创建游标对象 + cursor = conn.cursor() + + # 执行 SQL 查询 + query = "SELECT * FROM table_name" + cursor.execute(query) + + # 获取查询结果 + result = cursor.fetchall() + + # 输出结果 + for row in result: + print(row) + + # 关闭游标和连接 + cursor.close() + conn.close() + +mDBM = DBManager() +mDBM.connect() + +if __name__ == "__main__": + lista = set([1,2,3,4,5,6,7]) + listb = set([4,6,8,9,10]) + nadd = lista -listb + ndel = listb -lista + for one in nadd: + print(one) \ No newline at end of file diff --git a/DataStruct.py b/core/DataStruct.py similarity index 100% rename from DataStruct.py rename to core/DataStruct.py diff --git a/core/ModelManager.py b/core/ModelManager.py new file mode 100644 index 0000000..7ebc4fb --- /dev/null +++ b/core/ModelManager.py @@ -0,0 +1,167 @@ +# 导入代码依赖 +import time +import av +import os +import cv2 +import torch +import numpy as np +import threading +import importlib.util +from core.DBManager import mDBM,DBManager +from myutils.MyLogger_logger import LogHandler +from myutils.ConfigManager import myCongif + + + +class VideoCaptureWithFPS: + def __init__(self, src=0, target_fps=10): + self.cap = cv2.VideoCapture(src) + self.target_fps = target_fps + self.frame_interval = 1.0 / target_fps + self.last_frame_time = time.time() + + def read(self): + current_time = time.time() + elapsed_time = current_time - self.last_frame_time + + if elapsed_time < self.frame_interval: #小于间隔时间会休眠 + time.sleep(self.frame_interval - elapsed_time) + + self.last_frame_time = time.time() + ret, frame = self.cap.read() + return ret, frame + + def release(self): + self.cap.release() + self.cap = None + + +class ModelManager: + def __init__(self): + self.verify_list = {} + self.bRun = True + self.logger = LogHandler().get_logger("ModelManager") + # 本地YOLOv5仓库路径 + self.yolov5_path = myCongif.get_data("yolov5_path") + self.buflen = myCongif.get_data("buffer_len") + + def _open_view(self,url,itype): #打开摄像头 0--USB摄像头,1-RTSP,2-海康SDK + if itype == 0: + cap = VideoCaptureWithFPS(int(url)) + elif itype == 1: + cap = VideoCaptureWithFPS(url) + else: + raise Exception("视频参数错误!") + return cap + + def _import_model(self,model_name,model_path): + '''根据路径,动态导入模块''' + if os.path.exists(model_path): + module_spec = importlib.util.spec_from_file_location(model_name, model_path) + module = importlib.util.module_from_spec(module_spec) + module_spec.loader.exec_module(module) + else: + self.logger.error("{}文件不存在".format(model_path)) + return None + return module + + def dowork_thread(self,channel_id): + '''一个通道一个线程,关联的模型在一个线程检测''' + cap = None + #查询关联的模型 --- 在循环运行前把基础数据都准备好 + myDBM = DBManager() + myDBM.connection() + strsql = (f"select t1.model_id,t1.check_area,t1.polygon ,t2.duration_time,t2.proportion,t2.model_path " + f"from channel2model t1 left join model t2 on t1.model_id = t2.ID where t1.channel_id ={channel_id};") + myModels = myDBM.do_select(strsql) + #加载模型 --- 是不是要做个限制,一个视频通道关联算法模块的上限 --- 关联多了一个线程执行耗时较多,造成帧率太低,或者再多线程并发 #? + myModle_list = [] #存放模型对象List + myModle_data = [] #存放检测参数 + for model in myModels: + #基于基类实例化模块类 + m = self._import_model("",model[5]) #动态加载模型 -- 待完善 + myModle_list.append(m) + myModle_data.append(model) + + #开始循环检测 + #print(mydata[0],mydata[1],mydata[2],mydata[3]) # url type tag img_buffer + #[url,type,True,img_buffer] + while self.verify_list[channel_id][2]: #基于tag 作为运行标识。 线程里只是读,住线程更新,最多晚一轮,应该不用线程锁。需验证 + if not cap: #还没连接视频源 + try: + cap = self._open_view(self.verify_list[channel_id][0],self.verify_list[channel_id][1]) + except: + self.logger.error("参数错误,终止线程") + return + ret,frame = cap.read() + if not ret: + self.logger.warning("view disconnected. Reconnecting...") + cap.release() + cap = None + time.sleep(myCongif.get_data("cap_sleep_time")) + continue #没读到画面继续 + #图像处理 + img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + # img = frame.to_ndarray(format="bgr24") + #检查布防时间是否在布防时间内 + # 使用 模型 进行目标检测 + i_warn_count = 0 + for i in range(len(myModle_list)):#如果比较多可以考虑做线程进行检测 + model = myModle_list[i] + data = myModle_data[i] + # 进行模型检测 + detections,bwarn,warntext = model.verify(img,data) + #对识别结果要部要进行处理 + if bwarn: # 整个识别有产生报警 + # 绘制报警文本 + cv2.putText(img, 'Intruder detected!', (50, (i_warn_count+1)*50), + cv2.FONT_HERSHEY_SIMPLEX, 1,(0, 0, 255), 2) + i_warn_count += 1 + # 保存报警信息? --- 待完成 + self.save_warn() + # 推送报警? --- 待完成 + self.send_warn() + # 将检测结果图像转换为帧 -- 需要确认前面对img的处理都是累加的。 + new_frame = av.VideoFrame.from_ndarray(img, format="rgb24") + # 分析图片放入内存中 --- + #new_frame = cv2.resize(new_frame, (640, 480)) # 降低视频分辨率 + self.verify_list[channel_id][3].append(new_frame) + if len(self.verify_list[channel_id]) > self.buflen: # 保持缓冲区大小不超过10帧 + self.verify_list[channel_id][3].pop(0) + self.logger.debug("drop one frame!") + + + + def start_work(self,channel_id=0): + '''算法模型是在后台根据画面实时分析的''' + if channel_id ==0: + strsql = "select id,ulr,type from channel where is_work = 1;" #要考虑布防和撤防开关的的调整 + else: + strsql = f"select id,ulr,type from channel where is_work = 1 and id = {channel_id};" #单通道启动检测线程 + datas = mDBM.do_select(strsql) + for data in datas: + img_buffer = [] + run_data = [data[1],data[2],True,img_buffer] + self.verify_list[data[0]] = run_data #需要验证重复情况 + th_chn = threading.Thread(target=self.dowork_thread, args=(data[0],)) #一个视频通道一个线程,线程句柄暂时部保留 + th_chn.start() + + + def stop_work(self,channel_id=0): + if channel_id ==0: #所有线程停止 + for data in self.verify_list: + data[2] = False + del data[3] + time.sleep(2) + self.verify_list.clear() + else: + data = self.verify_list[channel_id] + data[2] = False + del data[3] + time.sleep(1) + del self.verify_list[channel_id] + + + +if __name__ == "__main__": + ModelManager().start_work() \ No newline at end of file diff --git a/SocketManager.py b/core/SocketManager.py similarity index 98% rename from SocketManager.py rename to core/SocketManager.py index c4ceaf1..43f5d18 100644 --- a/SocketManager.py +++ b/core/SocketManager.py @@ -1,9 +1,8 @@ import threading -import socket import socketserver from DataStruct import * -from util.MyLogger_logging import MyLogger -from DBManager import DBManager +from myutils.MyLogger_logging import MyLogger +from core.DBManager import DBManager import time m_BRun = False #线程运行标识 diff --git a/SystemManager.py b/core/SystemManager.py similarity index 100% rename from SystemManager.py rename to core/SystemManager.py diff --git a/core/Test.py b/core/Test.py new file mode 100644 index 0000000..29a4f6e --- /dev/null +++ b/core/Test.py @@ -0,0 +1,162 @@ +# 导入代码依赖 +import cv2 +import numpy as np +import ipywidgets as widgets +from IPython.display import display +import torch +from skvideo.io import vreader, FFmpegWriter +import IPython.display +from ais_bench.infer.interface import InferSession + +from det_utils import letterbox, scale_coords, nms + +def preprocess_image(image, cfg, bgr2rgb=True): + """图片预处理""" + img, scale_ratio, pad_size = letterbox(image, new_shape=cfg['input_shape']) + if bgr2rgb: + img = img[:, :, ::-1] + img = img.transpose(2, 0, 1) # HWC2CHW + img = np.ascontiguousarray(img, dtype=np.float32) + return img, scale_ratio, pad_size + + +def draw_bbox(bbox, img0, color, wt, names): + """在图片上画预测框""" + det_result_str = '' + for idx, class_id in enumerate(bbox[:, 5]): + if float(bbox[idx][4] < float(0.05)): + continue + img0 = cv2.rectangle(img0, (int(bbox[idx][0]), int(bbox[idx][1])), (int(bbox[idx][2]), int(bbox[idx][3])), + color, wt) + img0 = cv2.putText(img0, str(idx) + ' ' + names[int(class_id)], (int(bbox[idx][0]), int(bbox[idx][1] + 16)), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) + img0 = cv2.putText(img0, '{:.4f}'.format(bbox[idx][4]), (int(bbox[idx][0]), int(bbox[idx][1] + 32)), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) + det_result_str += '{} {} {} {} {} {}\n'.format( + names[bbox[idx][5]], str(bbox[idx][4]), bbox[idx][0], bbox[idx][1], bbox[idx][2], bbox[idx][3]) + return img0 + + +def get_labels_from_txt(path): + """从txt文件获取图片标签""" + labels_dict = dict() + with open(path) as f: + for cat_id, label in enumerate(f.readlines()): + labels_dict[cat_id] = label.strip() + return labels_dict + + +def draw_prediction(pred, image, labels): + """在图片上画出预测框并进行可视化展示""" + imgbox = widgets.Image(format='jpg', height=720, width=1280) + img_dw = draw_bbox(pred, image, (0, 255, 0), 2, labels) + imgbox.value = cv2.imencode('.jpg', img_dw)[1].tobytes() + display(imgbox) + + +def infer_image(img_path, model, class_names, cfg): + """图片推理""" + # 图片载入 + image = cv2.imread(img_path) + # 数据预处理 + img, scale_ratio, pad_size = preprocess_image(image, cfg) + # 模型推理 + output = model.infer([img])[0] + + output = torch.tensor(output) + # 非极大值抑制后处理 + boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"]) + pred_all = boxout[0].numpy() + # 预测坐标转换 + scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size)) + # 图片预测结果可视化 + draw_prediction(pred_all, image, class_names) + + +def infer_frame_with_vis(image, model, labels_dict, cfg, bgr2rgb=True): + # 数据预处理 + img, scale_ratio, pad_size = preprocess_image(image, cfg, bgr2rgb) + # 模型推理 + output = model.infer([img])[0] + + output = torch.tensor(output) + # 非极大值抑制后处理 + boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"]) + pred_all = boxout[0].numpy() + # 预测坐标转换 + scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size)) + # 图片预测结果可视化 + img_vis = draw_bbox(pred_all, image, (0, 255, 0), 2, labels_dict) + return img_vis + + +def img2bytes(image): + """将图片转换为字节码""" + return bytes(cv2.imencode('.jpg', image)[1]) + + +def infer_video(video_path, model, labels_dict, cfg): + """视频推理""" + image_widget = widgets.Image(format='jpeg', width=800, height=600) + display(image_widget) + + # 读入视频 + cap = cv2.VideoCapture(video_path) + while True: + ret, img_frame = cap.read() + if not ret: + break + # 对视频帧进行推理 + image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg, bgr2rgb=True) + image_widget.value = img2bytes(image_pred) + + +def infer_camera(model, labels_dict, cfg): + """外设摄像头实时推理""" + def find_camera_index(): + max_index_to_check = 10 # Maximum index to check for camera + + for index in range(max_index_to_check): + cap = cv2.VideoCapture(index) + if cap.read()[0]: + cap.release() + return index + + # If no camera is found + raise ValueError("No camera found.") + + # 获取摄像头 + camera_index = find_camera_index() + cap = cv2.VideoCapture(camera_index) + # 初始化可视化对象 + image_widget = widgets.Image(format='jpeg', width=1280, height=720) + display(image_widget) + while True: + # 对摄像头每一帧进行推理和可视化 + _, img_frame = cap.read() + image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg) + image_widget.value = img2bytes(image_pred) + +if __name__ == "__main__": + cfg = { + 'conf_thres': 0.4, # 模型置信度阈值,阈值越低,得到的预测框越多 + 'iou_thres': 0.5, # IOU阈值,高于这个阈值的重叠预测框会被过滤掉 + 'input_shape': [640, 640], # 模型输入尺寸 + } + + model_path = 'yolo.om' + label_path = './coco_names.txt' + # 初始化推理模型 + model = InferSession(0, model_path) + labels_dict = get_labels_from_txt(label_path) + + infer_mode = 'video' + + if infer_mode == 'image': + img_path = 'world_cup.jpg' + infer_image(img_path, model, labels_dict, cfg) + elif infer_mode == 'camera': + infer_camera(model, labels_dict, cfg) + elif infer_mode == 'video': + video_path = 'racing.mp4' + infer_video(video_path, model, labels_dict, cfg) \ No newline at end of file diff --git a/core/Upload_file.py b/core/Upload_file.py new file mode 100644 index 0000000..e7593af --- /dev/null +++ b/core/Upload_file.py @@ -0,0 +1,24 @@ +from quart import Blueprint, render_template, request, redirect, url_for, flash, current_app +import os +import subprocess +from werkzeug.utils import secure_filename +from myutils.ConfigManager import myCongif + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in myCongif.get_data('ALLOWED_EXTENSIONS') + +#对上传的系统升级包进行检查 type:1--系统升级包,2--算法升级包 +def check_file(filepath,type): + pass + +def update_system(filepath): #系统升级 + pass + +def updata_model(filepath): #算法模型升级或新增 + try: + # 假设我们解压并运行一个升级脚本 + subprocess.run(['unzip', '-o', filepath, '-d', '/path/to/upgrade/directory'], check=True) + subprocess.run(['/path/to/upgrade/directory/upgrade_script.sh'], check=True) + return True, 'Upgrade completed successfully.' + except subprocess.CalledProcessError as e: + return False, str(e) \ No newline at end of file diff --git a/core/ViewManager.py b/core/ViewManager.py new file mode 100644 index 0000000..8282dab --- /dev/null +++ b/core/ViewManager.py @@ -0,0 +1,59 @@ +import requests +import time +import threading +from core.DBManager import DBManager +from myutils.MyLogger_logger import LogHandler +from myutils.ConfigManager import myCongif + +class ViewManager: + + def __init__(self): + self.thcheck = False + self.lock = threading.Lock() + self.logger = LogHandler().get_logger("ViewManager") + + def check_rtsp_url(self,rtsp_url): #通过发送http请求判断rtsp流的在线状态 + http_url = rtsp_url.replace("rtsp://", "http://") + try: + response = requests.options(http_url, timeout=5) + if response.status_code == 200: + return True + except requests.RequestException: + pass + return False + + def check_all_urls(self): #? + strsql = "select ID,ulr from channel;" + datas = self.mDBM.do_select(strsql) + IDs = [row[0] for row in datas] + urls = [row[1] for row in datas] + #print(IDs,urls) + for i in range(len(urls)): + url = urls[i] + ID = IDs[i] + result = self.check_rtsp_url(url) + iret = 1 if result else 0 + strsql = f"update channel set status={iret} where ID={ID};" + self.logger.debug(strsql) + self.mDBM.do_sql(strsql) + + def start_check_rtsp(self, interval=myCongif.get_data('RTSP_Check_Time')): #通过线程循环检查添加的RTSP地址在线情况 + def run(): + self.mDBM = DBManager() + self.mDBM.connect() + while True: + self.check_all_urls() + time.sleep(interval) + + if self.thcheck == False: + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + self.thcheck = True + + +mVManager = ViewManager() + +if __name__ =='__main__': + mVManager.start_check_rtsp() + time.sleep(65) \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 5141d35..0000000 --- a/main.py +++ /dev/null @@ -1,13 +0,0 @@ -from util.ConfigManager import myCongif -from util.MyLogger_logger import LogHandler - -from web import create_app - -app = create_app() - -if __name__ == '__main__': - # logger = LogHandler().get_logger("main") - # logger.debug("debug") - # logger.info("info") - app.run(debug=True) - diff --git a/model/mode_test/yoloV5.py b/model/mode_test/yoloV5.py new file mode 100644 index 0000000..c85e556 --- /dev/null +++ b/model/mode_test/yoloV5.py @@ -0,0 +1,64 @@ +import torch +import cv2 +import numpy as np +from shapely.geometry import Point, Polygon +import os + +# # 自定义模型文件的路径 +# model_path = 'yolov5s.pt' # 假设模型文件名为 yolov5s.pt 并与执行文件在同一个目录 +# # 本地YOLOv5仓库路径 +# repo_path = '../base_model/yolov5' +# 自定义模型文件的路径 +print(f"Current working directory (yolov5.py): {os.getcwd()}") +model_path = 'D:/Project/FristProject/model/mode_test/yolov5s.pt' # 假设模型文件名为 yolov5s.pt 并与执行文件在同一个目录 +# 本地YOLOv5仓库路径 +repo_path = 'D:/Project/FristProject/model/base_model/yolov5' + +# 加载自定义模型 +model = torch.hub.load(repo_path, 'custom', path=model_path, source='local') + +# 定义监控区域(多边形顶点) +region_points = [(100, 100), (500, 100), (500, 400), (100, 400)] +polygon = Polygon(region_points) + +# 打开摄像头 +cap = cv2.VideoCapture(0) + +def is_point_in_polygon(point, polygon): + return polygon.contains(Point(point)) + +while True: + ret, frame = cap.read() + if not ret: + break + + # 进行推理 + results = model(frame) + detections = results.pandas().xyxy[0] + + # 绘制监控区域 + cv2.polylines(frame, [np.array(region_points, np.int32)], isClosed=True, color=(0, 255, 0), thickness=2) + + for _, row in detections.iterrows(): + if row['name'] == 'person': + # 获取人员检测框的中心点 + x_center = (row['xmin'] + row['xmax']) / 2 + y_center = (row['ymin'] + row['ymax']) / 2 + + if is_point_in_polygon((x_center, y_center), polygon): + # 触发报警 + cv2.putText(frame, 'Alert: Intrusion Detected', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) + print('Alert: Intrusion Detected') + + # 绘制检测框 + cv2.rectangle(frame, (int(row['xmin']), int(row['ymin'])), (int(row['xmax']), int(row['ymax'])), (255, 0, 0), 2) + cv2.circle(frame, (int(x_center), int(y_center)), 5, (0, 0, 255), -1) + + # 显示结果 + cv2.imshow('Frame', frame) + + if cv2.waitKey(1) & 0xFF == ord('q'): + break + +cap.release() +cv2.destroyAllWindows() diff --git a/model/plugins/ModelBase.py b/model/plugins/ModelBase.py index 6cee3cf..0875b1c 100644 --- a/model/plugins/ModelBase.py +++ b/model/plugins/ModelBase.py @@ -1,17 +1,48 @@ -from abc import abstractmethod +from abc import abstractmethod,ABC +from shapely.geometry import Point, Polygon +import numpy as np +import cv2 -class ModelBase(object): +class ModelBase(ABC): def __init__(self): - pass + self.name = None #基于name来查询,用户对模型的配置参数,代表着模型名称需要唯一 2024-6-18 -逻辑还需要完善和验证 + self.version = None + self.model_type = None # 模型类型 1-图像分类,2-目标检测(yolov5),3-分割模型,4-关键点 + self.do_map = { # 定义插件的入口函数 -- + # POCType.POC: self.do_verify, + # POCType.SNIFFER: self.do_sniffer, + # POCType.BRUTE: self.do_brute + } def __del__(self): + print("资源释放") + + def draw_polygon(self, img, polygon_points,color=(0, 255, 0)): + self.polygon = Polygon(polygon_points) + + points = np.array([self.polygon.exterior.coords], dtype=np.int32) + cv2.polylines(img, points, isClosed=True, color=color, thickness=2) + + def is_point_in_region(self, point): + '''判断点是否在区域内,需要先执行draw_polygon''' + if self.polygon: + return self.polygon.contains(Point(point)) + else: + return False + + def save_warn(self): + '''保存报警信息''' + pass + + def send_warn(self): + '''发送报警信息''' pass - ''' - 模型处理的标准接口 - 1.输入标准化 - 2.输出标准化 - ''' @abstractmethod - def doWork(self,image,mode,class_name,cfg,isdraw_box=False,bgr2rgb=True): + def verify(self,image,data): + ''' + :param image: 需要验证的图片 + :param data: select t1.model_id,t1.check_area,t1.polygon ,t2.duration_time,t2.proportion,t2.model_path + :return: detections,bwarn,warntext + ''' pass \ No newline at end of file diff --git a/model/plugins/RYRQ_Model.py b/model/plugins/RYRQ_Model.py index 9eabfea..50ee80e 100644 --- a/model/plugins/RYRQ_Model.py +++ b/model/plugins/RYRQ_Model.py @@ -1,5 +1,42 @@ from model.plugins.ModelBase import ModelBase - +from myutils.ConfigManager import myCongif +from shapely.geometry import Point, Polygon +import torch +import cv2 +import av +import numpy as np class Model(ModelBase): - def doWork(self,image,mode,class_name,cfg,isdraw_box=False,bgr2rgb=True): - print("RYRQ_Model") + def __init__(self,path): + super().__init__() + self.name = "人员入侵" + self.version = "V1.0" + self.model_type = 2 + #实例化模型 + self.model = torch.hub.load(myCongif.get_data("yolov5_path"), 'custom', path=path, source='local') + + def verify(self,image,data): + results = self.model(image) # 进行模型检测 --- 需要统一接口 + detections = results.pandas().xyxy[0].to_dict(orient="records") + bwarn = False + warn_text = "" + #是否有检测区域,有先绘制检测区域 由于在该函数生成了polygon对象,所有需要在检测区域前调用。 + if data[1] == 1: + self.draw_polygon(image,data[2],(0, 255, 0)) + # 绘制检测结果 --- 也需要封装在类里, + for det in detections: + if det['name'] == 'person': #标签是人 + x1, y1, x2, y2 = int(det['xmin']), int(det['ymin']), int(det['xmax']), int(det['ymax']) + # 绘制目标识别的锚框 + cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) + if data[1] == 1: # 指定了检测区域 + x_center = (x1 + x2) / 2 + y_center = (y1 + y2) / 2 + #绘制中心点? + cv2.circle(image, (int(x_center), int(y_center)), 5, (0, 0, 255), -1) + #判断是否区域点 + if not self.is_point_in_region((x_center, y_center)): + continue #没产生报警-继续 + #产生报警 + bwarn = True + warn_text = "Intruder detected!" + return detections,bwarn,warn_text \ No newline at end of file diff --git a/model/plugins/yolov5-1.py b/model/plugins/yolov5-1.py new file mode 100644 index 0000000..76a9333 --- /dev/null +++ b/model/plugins/yolov5-1.py @@ -0,0 +1,155 @@ +# 导入代码依赖 +import cv2 +import numpy as np +from myutils.ConfigManager import myCongif +import os +import torch + +import ipywidgets as widgets +from IPython.display import display +from skvideo.io import vreader, FFmpegWriter +import IPython.display +from ais_bench.infer.interface import InferSession +from det_utils import letterbox, scale_coords, nms + +def preprocess_image(image, cfg, bgr2rgb=True): + """图片预处理""" + img, scale_ratio, pad_size = letterbox(image, new_shape=cfg['input_shape']) + if bgr2rgb: + img = img[:, :, ::-1] + img = img.transpose(2, 0, 1) # HWC2CHW + img = np.ascontiguousarray(img, dtype=np.float32) + return img, scale_ratio, pad_size + +def draw_bbox(bbox, img0, color, wt, names): + """在图片上画预测框""" + det_result_str = '' + for idx, class_id in enumerate(bbox[:, 5]): + if float(bbox[idx][4] < float(0.05)): + continue + img0 = cv2.rectangle(img0, (int(bbox[idx][0]), int(bbox[idx][1])), (int(bbox[idx][2]), int(bbox[idx][3])), + color, wt) + img0 = cv2.putText(img0, str(idx) + ' ' + names[int(class_id)], (int(bbox[idx][0]), int(bbox[idx][1] + 16)), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) + img0 = cv2.putText(img0, '{:.4f}'.format(bbox[idx][4]), (int(bbox[idx][0]), int(bbox[idx][1] + 32)), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) + det_result_str += '{} {} {} {} {} {}\n'.format( + names[bbox[idx][5]], str(bbox[idx][4]), bbox[idx][0], bbox[idx][1], bbox[idx][2], bbox[idx][3]) + return img0 + +def get_labels_from_txt(path): + """从txt文件获取图片标签""" + labels_dict = dict() + with open(path) as f: + for cat_id, label in enumerate(f.readlines()): + labels_dict[cat_id] = label.strip() + return labels_dict + +def draw_prediction(pred, image, labels): + """在图片上画出预测框并进行可视化展示""" + imgbox = widgets.Image(format='jpg', height=720, width=1280) + img_dw = draw_bbox(pred, image, (0, 255, 0), 2, labels) + imgbox.value = cv2.imencode('.jpg', img_dw)[1].tobytes() + display(imgbox) + +def infer_image(img_path, model, class_names, cfg): + """图片推理""" + # 图片载入 + image = cv2.imread(img_path) + # 数据预处理 + img, scale_ratio, pad_size = preprocess_image(image, cfg) + # 模型推理 + output = model.infer([img])[0] + + output = torch.tensor(output) + # 非极大值抑制后处理 + boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"]) + pred_all = boxout[0].numpy() + # 预测坐标转换 + scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size)) + # 图片预测结果可视化 + draw_prediction(pred_all, image, class_names) + +def infer_frame_with_vis(image, model, labels_dict, cfg, bgr2rgb=True): + # 数据预处理 + img, scale_ratio, pad_size = preprocess_image(image, cfg, bgr2rgb) + # 模型推理 + output = model.infer([img])[0] + + output = torch.tensor(output) + # 非极大值抑制后处理 + boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"]) + pred_all = boxout[0].numpy() + # 预测坐标转换 + scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size)) + # 图片预测结果可视化 + img_vis = draw_bbox(pred_all, image, (0, 255, 0), 2, labels_dict) + return img_vis + +def img2bytes(image): + """将图片转换为字节码""" + return bytes(cv2.imencode('.jpg', image)[1]) + +def infer_video(video_path, model, labels_dict, cfg): + """视频推理""" + image_widget = widgets.Image(format='jpeg', width=800, height=600) + display(image_widget) + + # 读入视频 + cap = cv2.VideoCapture(video_path) + while True: + ret, img_frame = cap.read() + if not ret: + break + # 对视频帧进行推理 + image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg, bgr2rgb=True) + image_widget.value = img2bytes(image_pred) + +def infer_camera(model, labels_dict, cfg): + """外设摄像头实时推理""" + def find_camera_index(): + max_index_to_check = 10 # Maximum index to check for camera + + for index in range(max_index_to_check): + cap = cv2.VideoCapture(index) + if cap.read()[0]: + cap.release() + return index + + # If no camera is found + raise ValueError("No camera found.") + + # 获取摄像头 + camera_index = find_camera_index() + cap = cv2.VideoCapture(camera_index) + # 初始化可视化对象 + image_widget = widgets.Image(format='jpeg', width=1280, height=720) + display(image_widget) + while True: + # 对摄像头每一帧进行推理和可视化 + _, img_frame = cap.read() + image_pred = infer_frame_with_vis(img_frame, model, labels_dict, cfg) + image_widget.value = img2bytes(image_pred) + +if __name__ == "__main__": + cfg = { + 'conf_thres': 0.4, # 模型置信度阈值,阈值越低,得到的预测框越多 + 'iou_thres': 0.5, # IOU阈值,高于这个阈值的重叠预测框会被过滤掉 + 'input_shape': [640, 640], # 模型输入尺寸 + } + model_path = os.path.join(myCongif.get_data('weight_path'),'/yolov5-1/yolov5s.pt') + label_path = os.path.join(myCongif.get_data('weight_path'),'/yolov5-1/coco_names.txt') + # 初始化推理模型 + model = InferSession(0, model_path) + labels_dict = get_labels_from_txt(label_path) + + infer_mode = 'video' + + if infer_mode == 'image': + img_path = 'world_cup.jpg' + infer_image(img_path, model, labels_dict, cfg) + elif infer_mode == 'camera': + infer_camera(model, labels_dict, cfg) + elif infer_mode == 'video': + video_path = 'racing.mp4' + infer_video(video_path, model, labels_dict, cfg) \ No newline at end of file diff --git a/util/ConfigManager.py b/myutils/ConfigManager.py similarity index 54% rename from util/ConfigManager.py rename to myutils/ConfigManager.py index 207264e..f914589 100644 --- a/util/ConfigManager.py +++ b/myutils/ConfigManager.py @@ -7,16 +7,15 @@ import yaml,os class ConfigManager(): def __init__(self,congif_path='config.yaml'): self.ok = False + if os.path.exists(congif_path): self.file_path = congif_path - self.data = self.read_yaml() - self.ok = True + self.ok = self.read_yaml() else: congif_path = "../" + congif_path if os.path.exists(congif_path): self.file_path = congif_path - self.data = self.read_yaml() - self.ok = True + self.ok= self.read_yaml() else: raise Exception('没有找到%s文件路径'%congif_path) print("ConfigManager实例化") @@ -24,31 +23,34 @@ class ConfigManager(): def __del__(self): print("ConfigManager销毁") - #已调整方案--配置文件的初始化工作放到构造函数中处理。 - def my_init(self,file_path): - if os.path.exists(file_path): - self.file_path = file_path - self.data = self.read_yaml() - self.ok = True - else: - raise Exception('没有找到%s文件路径'%file_path) - def read_yaml(self): with open(self.file_path,'r',encoding='utf_8') as f: - p = f.read() - return p + self.data = yaml.safe_load(f) + return True + return False + ''' + 以.作为层级节点分割符 + ''' def get_data(self,pwd=None): if self.ok: - result = yaml.load(self.data,Loader=yaml.FullLoader) - if pwd == None: - return result - else: - return result.get(pwd) + if pwd is None: + return None + nodes = pwd.split('.') + current_node= self.data + for node in nodes: + if node in current_node: + current_node = current_node[node] + else: + print(f"Node {node} not found in the YAML data.") + return None + + return current_node + #这种方法实现的单例优点是简单,但不能动态创建实例,程序加载时就已实例化。 myCongif = ConfigManager() if __name__ == '__main__': - r = myCongif.get_data('url') + r = myCongif.get_data('mysql.host1') print(r) \ No newline at end of file diff --git a/util/MyLogger_logger.py b/myutils/MyLogger_logger.py similarity index 96% rename from util/MyLogger_logger.py rename to myutils/MyLogger_logger.py index 829d516..ba22a33 100644 --- a/util/MyLogger_logger.py +++ b/myutils/MyLogger_logger.py @@ -1,7 +1,7 @@ from loguru import logger import os from functools import wraps -from util.ConfigManager import myCongif +from myutils.ConfigManager import myCongif ''' @logger.catch() 异常捕获注解 diff --git a/util/MyLogger_logging.py b/myutils/MyLogger_logging.py similarity index 100% rename from util/MyLogger_logging.py rename to myutils/MyLogger_logging.py diff --git a/myutils/ReManager.py b/myutils/ReManager.py new file mode 100644 index 0000000..8cb583f --- /dev/null +++ b/myutils/ReManager.py @@ -0,0 +1,67 @@ +import ipaddress +import re +''' +正则管理类,用来实现对各类字符串进行合法性校验 +''' +class ReManager(): + def is_valid_ip(self,ip): + try: + ipaddress.ip_address(ip) + return True + except ValueError: + return False + + def is_valid_subnet_mask(self,subnet_mask): + # 将子网掩码字符串转换为整数列表 + bits = [int(b) for b in subnet_mask.split('.')] + if len(bits) != 4: + return False + + # 检查每个部分是否在0-255范围内 + if not all(0 <= b <= 255 for b in bits): + return False + + # 检查子网掩码是否连续为1后跟连续为0 + mask_int = (bits[0] << 24) + (bits[1] << 16) + (bits[2] << 8) + bits[3] + binary_mask = bin(mask_int)[2:].zfill(32) + ones_count = binary_mask.count('1') + if not all(bit == '1' for bit in binary_mask[:ones_count]) or not all( + bit == '0' for bit in binary_mask[ones_count:]): + return False + return True + + def is_valid_port(self,port): + try: + port = int(port) # 尝试将端口转换为整数 + if 1 <= port <= 65535: # 检查端口号是否在合法范围内 + return True + else: + return False + except ValueError: + # 如果端口不是整数,返回False + return False + + def is_valid_rtsp_url(self,url): + pattern = re.compile( + r'^(rtsp:\/\/)' # Start with rtsp:// + r'(?P(?:[a-zA-Z0-9_\-\.]+)|(?:\[[a-fA-F0-9:]+\]))' # Hostname or IPv4/IPv6 address + r'(?::(?P\d+))?' # Optional port number + r'(?P\/[a-zA-Z0-9_\-\.\/]*)?$' # Optional path + ) + match = pattern.match(url) + return match is not None + +mReM = ReManager() + +if __name__ == "__main__": + urls = [ + "rtsp://192.168.1.1:554/stream1", + "rtsp://example.com/stream", + "rtsp://[2001:db8::1]:554/stream", + "rtsp://localhost", + "http://example.com", + "ftp://example.com/stream" + ] + + for url in urls: + print(f"{url}: {mReM.is_valid_rtsp_url(url)}") \ No newline at end of file diff --git a/myutils/loader.py b/myutils/loader.py new file mode 100644 index 0000000..db508f6 --- /dev/null +++ b/myutils/loader.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +__author__ = 'ZL' +import importlib.util +import os + +def import_source(module_name,module_path): + module_spec = importlib.util.spec_from_file_location(module_name, module_path) #根据给定的模块名和路径创建一个模块规范对象 + module = importlib.util.module_from_spec(module_spec) #根据模块规范创建一个新模块对象 + module_spec.loader.exec_module(module) #执行模块的代码,将其导入到新创建的模块对象中 + return module + +#遍历指定目录的文件,并去除掉一部分文件 +def walk_py(path): + for dir_path, dir_names, filenames in os.walk(path): + if dir_path.endswith("__pycache__"): + continue + + for f in filenames: + if f.startswith('_') or f.endswith('_.py'): + continue + + split = f.split('.') + + if len(split) == 2 and split[1] == 'py': + abspath = os.path.abspath(os.path.join(dir_path, f)) + yield abspath, split[0] + + +def load_plugins(path): + plugins = [] + for file_path, name in walk_py(path): + try: + module = import_source(spec="repoc_plugins", path=file_path) + Plugin = getattr(module, 'Plugin') + plugin = Plugin() + setattr(plugin, '_plugin_name', name) + plugins.append(plugin) + except Exception as e: + print("load plugin error from {}".format(file_path)) + + return plugins \ No newline at end of file diff --git a/util/logs/2024-05-25.log b/myutils/logs/2024-05-25.log similarity index 100% rename from util/logs/2024-05-25.log rename to myutils/logs/2024-05-25.log diff --git a/util/logs/app.log b/myutils/logs/app.log similarity index 100% rename from util/logs/app.log rename to myutils/logs/app.log diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..849dc86 Binary files /dev/null and b/requirements.txt differ diff --git a/run.py b/run.py new file mode 100644 index 0000000..427a6ef --- /dev/null +++ b/run.py @@ -0,0 +1,17 @@ +from myutils.ConfigManager import myCongif +from myutils.MyLogger_logger import LogHandler +from core.ViewManager import mVManager +from web import create_app +import os + + +#print(f"Current working directory (run.py): {os.getcwd()}") +web = create_app() + +if __name__ == '__main__': + # logger = LogHandler().get_logger("main") + # logger.debug("debug") + # logger.info("info") + mVManager.start_check_rtsp() + web.run(debug=True,port=5001) + diff --git a/web/API/__init__.py b/web/API/__init__.py new file mode 100644 index 0000000..bdf772c --- /dev/null +++ b/web/API/__init__.py @@ -0,0 +1,4 @@ +from quart import Blueprint +#定义模块 +api = Blueprint('api',__name__) +from . import user,system,viedo,channel,model diff --git a/web/API/channel.py b/web/API/channel.py new file mode 100644 index 0000000..4541e33 --- /dev/null +++ b/web/API/channel.py @@ -0,0 +1,290 @@ +import json +from quart import jsonify, request +from . import api +from web.common.utils import login_required +from core.DBManager import mDBM +from myutils.ReManager import mReM + +@api.route('/channel/tree',methods=['GET']) +@login_required +async def channel_tree(): #获取通道树 + strsql = ("select t2.area_name ,t1.ID ,t1.channel_name from channel t1 left join area t2 on t1.area_id = t2.id;") + data = mDBM.do_select(strsql) + channel_tree = [{"area_name": channel[0], "ID": channel[1],"channel_name":channel[2]} for channel in data] + return jsonify(channel_tree) + +@api.route('/channel/list',methods=['GET']) + +async def channel_list(): #获取通道列表 --分页查询,支持区域和通道名称关键字查询 + strsql = ("select t2.area_name ,t1.ID ,t1.channel_name ,t1.ulr ,t1.'type' ,t1.status ,t1.element_id" + " from channel t1 left join area t2 on t1.area_id = t2.id;") + data = mDBM.do_select(strsql) + channel_list = [{"area_name": channel[0], "ID": channel[1], "channel_name": channel[2], "ulr": channel[3], + "type": channel[4], "status": channel[5], "element_id": channel[6]} for channel in data] + return jsonify(channel_list) + +@api.route('/channel/info',methods=['GET']) +@login_required +async def channel_info(): #获取通道信息 ---- list已获取详情 + return jsonify(1) + +@api.route('/channel/add',methods=['POST']) +@login_required +async def channel_add(): #新增通道 + area_id = (await request.form)['area_id'] + channel_name = (await request.form)['channel_name'] + url = (await request.form)['url'] + if mReM.is_valid_rtsp_url(url) is not True: + reStatus = 0 + reMsg = 'rtsp地址不合法' + return jsonify({'status': reStatus, 'msg': reMsg}) + strsql = f"select area_name from area where id = {area_id};" + ret = mDBM.do_select(strsql,1) + if ret: + strsql = (f"INSERT INTO channel (area_id,channel_name,ulr,'type',status) values " + f"({area_id},'{channel_name}','{url}',1,0);") + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '添加通道成功' + else: + reStatus = 0 + reMsg = '添加通道失败,请联系技术支持!' + else: + reStatus = 0 + reMsg = '错误的通道ID,请修改' + return jsonify({'status':reStatus,'msg':reMsg}) + +@api.route('/channel/change',methods=['POST']) +@login_required +async def channel_change(): #修改通道信息 + area_id = (await request.form)['area_id'] + channel_id = (await request.form)['channel_id'] + channel_name = (await request.form)['channel_name'] + url = (await request.form)['url'] + if mReM.is_valid_rtsp_url(url) is not True: + reStatus = 0 + reMsg = 'rtsp地址不合法' + return jsonify({'status': reStatus, 'msg': reMsg}) + strsql = f"select area_name from area where id = {area_id};" + ret = mDBM.do_select(strsql, 1) + if ret: + strsql = f"UPDATE channel SET area_id={area_id},channel_name='{channel_name}',ulr='{url}' where ID={channel_id};" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '修改通道信息成' + else: + reStatus = 0 + reMsg = '修改通道信息失败,请联系技术支持!' + else: + reStatus = 0 + reMsg = '错误的通道ID,请修改' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/del',methods=['POST']) +@login_required +async def channel_del(): #删除通道 + channel_id = (await request.form)['channel_id'] + #删除该通道和算法的关联信息:布防时间,算法模型数据----使用外键级联删除会方便很多,只要一个删除就可以 + ret = mDBM.delchannel(channel_id) + if ret == True: + reStatus = 1 + reMsg = '删除通道信息成' + else: + reStatus = 0 + reMsg = '删除通道信息失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/check',methods=['GET']) +@login_required +async def channel_check(): #检查通道视频的在线情况--可以获取全部 ID-0,全部,1*具体通道ID--10分钟会更新一次在线状态 + ID = request.args.get('ID') + if ID: + ID = int(ID) + if ID==0: + strsql = "select ID,status from channel;" + elif ID > 0: + strsql = f"select ID,status from channel where ID={ID};" + else: + reStatus = 0 + reMsg = "参数错误" + return jsonify({'status': reStatus, 'msg': reMsg}) + datas = mDBM.do_select(strsql) + reStatus = 1 + reMsg = [{'ID': data[0], 'status': data[1]} for data in datas] + return jsonify({'status': reStatus, 'msg': reMsg}) + else: + reStatus = 0 + reMsg = "参数错误" + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/area/list',methods=['GET']) +@login_required +async def channel_area_list(): #获取区域列表 + strsql = "select * from area;" + datas = mDBM.do_select(strsql) + reMsg = [{'id': data[0], 'area_name': data[1]} for data in datas] + return jsonify(reMsg) + +@api.route('/channel/area/change',methods=['POST']) +@login_required +async def channel_area_change(): #修改区域名称 + area_id = (await request.form)['id'] + area_name = (await request.form)['name'] + strsql = f"update area set area_name='{area_name}' where id = {area_id};" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '修改通道名称成功' + else: + reStatus = 0 + reMsg = '修改通道名称失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/area/del',methods=['POST']) +@login_required +async def channel_area_del(): #删除区域 + area_id = (await request.form)['id'] + strsql = f"select * from channel where area_id={area_id};" + datas = mDBM.do_select(strsql) + if datas: + reStatus = 0 + reMsg = '该区域下配置了通道,请先删除通道后,再删除区域!' + else: + strsql = f"delete from area where id={area_id};" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '删除区域成功' + else: + reStatus = 0 + reMsg = '删除区域失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/area/add',methods=['POST']) +@login_required +async def channel_area_add(): #添加区域 + area_name = (await request.form)['name'] + strsql = f"insert into area (area_name) values ('{area_name}');" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '新增区域成功' + else: + reStatus = 0 + reMsg = '新增区域失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/model/linklist',methods=['GET']) +@login_required +async def channel_model_linklist(): #获取算法列表 --关联算法时展示 + ID = request.args.get('ID') #通道ID + strsql = (f"select t1.ID,t2.model_name from channel2model t1 left join model t2 " + f"on t1.model_id=t2.ID where channel_id={ID};") + datas = mDBM.do_select(strsql) + reMsg = {} + if datas: + reMsg = [{'ID': data[0], 'model_name': data[1]} for data in datas] + return jsonify(reMsg) + +@api.route('/channel/model/list',methods=['GET']) +@login_required +async def channel_model_list(): #获取算法列表 + strsql = "select ID,model_name,version from model;" + datas = mDBM.do_select(strsql) + reMsg = {} + if datas: + reMsg = [{'ID': data[0], 'model_name': data[1],'version':data[2]} for data in datas] + return jsonify(reMsg) + +@api.route('/channel/model/linkmodel',methods=['POST']) +@login_required +async def channel_model_linkmodel(): #获取算法列表 --关联算法时展示 + channel_id = (await request.form)['channel_id'] + model_list = json.loads((await request.form)['model_list']) + #? 需要对channel_id和model_list的是否在数据库要进行一个检查 + reStatus = 0 + reMsg = '提交的参数错误,请检查!' + strsql = f"select ID from channel where ID={channel_id};" + if mDBM.do_select(strsql,1): #通道号在数据库中 + for model_id in model_list: + strsql = f"select ID from model where ID = {model_id};" + if not mDBM.do_select(strsql,1): + return jsonify({'status': reStatus, 'msg': reMsg}) + ret =mDBM.updateC2M(channel_id,model_list) + if ret == True: + reStatus = 1 + reMsg = '修改关联算法成功' + else: + reStatus = 0 + reMsg = '修改关联算法失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/model/getarea',methods=['GET']) +@login_required +async def channel_model_getarea(): #获取算法区域的备注信息 + ID = request.args.get('ID') + strsql = f"select check_area,check_x,check_y,check_width,check_height from channel2model where ID={ID};" + data = mDBM.do_select(strsql,1) + if data: + reMsg = {'ID':ID,'check_area':data[0],'check_x':data[1],'check_y':data[2], + 'check_width':data[3],'check_height':data[4]} + else: + reMsg = {} + return jsonify(reMsg) + +@api.route('/channel/model/changearea',methods=['POST']) +@login_required +async def channel_model_changearea(): #修改算法区域信息 + ID = (await request.form)['ID'] + check_area = (await request.form)['check_area'] + check_x = (await request.form)['check_x'] + check_y = (await request.form)['check_y'] + check_width = (await request.form)['check_width'] + check_height = (await request.form)['check_height'] + strsql = (f"update channel2model set check_area={check_area},check_x={check_x},check_y={check_y}" + f",check_width={check_width},check_height={check_height} where ID={ID};") + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '修改算法检测区域成功' + else: + reStatus = 0 + reMsg = '新修改算法检测区域失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/channel/model/getschedule',methods=['GET']) +@login_required +async def channel_model_getschedule(): #获取算法的布防时间 + ID = request.args.get('ID') + strsql = f"select day,hour,status from schedule where channel2model_id={ID};" + datas = mDBM.do_select(strsql) + if datas: + reMsg = [{'day': data[0], 'hour': data[1],'status':data[2]} for data in datas] + else: + reMsg = {} + return jsonify(reMsg) + +@api.route('/channel/model/changeschedule',methods=['POST']) +@login_required +async def channel_model_changeschedule(): #修改算法的布防时间 + ID = (await request.form)['ID'] + schedule_data_str = (await request.form)['schedule_data'] + schedule_data = json.loads(schedule_data_str.replace("'", '"')) + for day,hours in schedule_data.items(): + for hour,status in enumerate(hours): + strsql = (f"insert into schedule (channel2model_id,day,hour,status) values ({ID},'{day}',{hour},{status})" + f" on conflict(channel2model_id,day,hour) do update set status=excluded.status;") + ret = mDBM.do_sql(strsql) + if ret == False: + reStatus = 0 + reMsg = '修改监测区域失败,请联系技术支持!' + return jsonify({'status': reStatus, 'msg': reMsg}) + reStatus = 1 + reMsg = '修改监测区域成功' + return jsonify({'status': reStatus, 'msg': reMsg}) + + + + diff --git a/web/API/model.py b/web/API/model.py new file mode 100644 index 0000000..e8461fa --- /dev/null +++ b/web/API/model.py @@ -0,0 +1,113 @@ +import os +from quart import jsonify, request,session,flash,redirect +from . import api +from web.common.utils import login_required +from core.DBManager import mDBM +from core.Upload_file import allowed_file,check_file,updata_model +from myutils.ConfigManager import myCongif +from werkzeug.utils import secure_filename +@api.route('/model/list',methods=['GET']) +@login_required +async def model_list(): #获取算法列表 + strsql = "select ID,model_name,version,duration_time,proportion from model;" + datas = mDBM.do_select(strsql) + reMsg = [{"ID":data[0],"name":data[1],"version":data[2],"duration_time":data[3], + "proportion":data[4]} for data in datas] + return jsonify(reMsg) + +@api.route('/model/upload',methods=['POST']) +@login_required +async def model_upload(): #上传算法文件--需要进行文件校验 + form = await request.form + model_name = form['model_name'] + files = await request.files + if 'file' not in files: + flash('参数错误') + return redirect(request.url) + file = files['file'] + if file.filename == '': + flash('没有选择文件') + return redirect(request.url) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file_path = os.path.join(myCongif.get_data('UPLOAD_FOLDER'), filename) + await file.save(file_path) + if check_file(file_path,2): #itype--2 是算法模型升级 + strsql = (f"insert into upgrade (itype,filepath,model_name) values (2,'{file_path}','{model_name}')" + f" on conflict(itype,model_name) do update set filepath=excluded.filepath;") + ret = mDBM.do_sql(strsql) + # session['model'] = file_path + if ret: + strsql = f"select id from upgrade where itype=2 and model_name='{model_name}';" + data=mDBM.do_select(strsql,1) + reStatus = data[0] + reMsg = "升级包上传成功" + else: + reStatus = 0 + reMsg = "升级包上传失败" + return jsonify({'status': reStatus, 'msg': reMsg}) + else: + flash('升级包不合法,请重新上传') + # return redirect(url_for('main.get_html', html='系统管理.html')) + return redirect(request.url) + else: + flash('只允许上传zip文件') + return redirect(request.url) + +@api.route('/model/add',methods=['POST']) +@login_required +async def model_add(): #新增算法,需要先上传算法文件 + model_name = (await request.form)['model_name'] + upgrade_id = (await request.form)['upgrade_id'] #升级文件ID + ###---需要根据升级包获取模型文件路径,算法版本号等信息,若根据upgrade_id获取不了对应的信息,则返回报错 + ### + strsql = f"select ID from model where model_name='{model_name}';" + data = mDBM.do_select(strsql,1) + if data: + reStatus = 0 + reMsg = "算法名称重复,请修改!" + else: + strsql = f"insert into model (model_name) values ('{model_name}');" #还有参数未写全 + ret = mDBM.do_sql(strsql) + if ret: + reStatus = 1 + reMsg = "新增算法成功!" + else: + reStatus = 0 + reMsg = "新增算法失败,请联系技术支持!" + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/model/upgrade',methods=['POST']) +@login_required +async def model_upgrade(): #升级算法,需要先上传算法文件 + return jsonify(1) + +@api.route('/model/config',methods=['GET']) +@login_required +async def model_config(): #获取算法的配置信息 --- list已经获取 + ID = request.args.get('ID') + strsql = f"select model_name,version,duration_time,proportion from model where ID={ID};" + data = mDBM.do_select(strsql,1) + reMsg = {"model_name":data[0],"version":data[1],"duration_time":data[2],"proportion":data[3]} + return jsonify(reMsg) + +@api.route('/model/changecnf',methods=['POST']) +@login_required +async def model_config_change(): #修改算法的配置信息 + ID = (await request.form)['ID'] + duration_time = (await request.form)['duration_time'] + proportion = float((await request.form)['proportion']) + if proportion>0 and proportion < 1: + strsql = f"update model set duration_time='{duration_time}',proportion='{proportion}' where ID={ID};" + ret = mDBM.do_sql(strsql) + if ret: + reStatus = 1 + reMsg = "修复算法配置成功" + else: + reStatus = 0 + reMsg = "修复算法配置失败" + else: + reStatus = 0 + reMsg = "占比需要为大于0,小于1的值" + return jsonify({'status': reStatus, 'msg': reMsg}) + diff --git a/web/API/system.py b/web/API/system.py new file mode 100644 index 0000000..05cae78 --- /dev/null +++ b/web/API/system.py @@ -0,0 +1,108 @@ +import os +from quart import jsonify, request,flash,redirect,url_for,session +from . import api +from web.common.utils import login_required +from core.DBManager import mDBM +from core.Upload_file import allowed_file,check_file,update_system +from myutils.ConfigManager import myCongif +from myutils.ReManager import mReM +from werkzeug.utils import secure_filename + +@api.route('/system/info',methods=['GET']) +@login_required +async def system_info(): #获取系统信息 + strsql = "select * from device;" + data = mDBM.do_select(strsql,1) + strRet = "" + if data: + strRet = {"ID":data[0],"version":data[1],"dev_ip":data[2],"dev_mask":data[3],"dev_gateway":data[4], + "server_ip":data[5],"server_port":data[6]} + return jsonify(strRet) #它将Python对象转换为JSON格式的字符串,并将其作为HTTP响应的主体返回给客户端, + # 同时设置正确的Content-Type响应头,表明响应主体是JSON格式的数据。 + +@api.route('/system/upload',methods=['POST']) +@login_required +async def system_upload(): #上传系统升级文件 + form = await request.form + files = await request.files + if 'file' not in files: + flash('参数错误') + return redirect(request.url) + file = files['file'] + if file.filename == '': + flash('没有选择文件') + return redirect(request.url) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file_path = os.path.join(myCongif.get_data('UPLOAD_FOLDER'), filename) + await file.save(file_path) + if check_file(file_path,1): + session['file'] = file_path + flash('升级包上传成功') + else: + flash('升级包不合法,请重新上传') + #return redirect(url_for('main.get_html', html='系统管理.html')) + return redirect(request.url) + else: + flash('只允许上传zip文件') + return redirect(request.url) + +@api.route('/system/upgrade',methods=['POST']) +@login_required +async def system_upgrade(): #系统升级 + if 'file' not in session: + reStatus = 0 + reMsg = "请先上传升级包文件" + else: + filepath = session['file'] + if update_system(filepath): + reStatus = 1 + reMsg = "系统升级成功" + else: + reStatus = 0 + reMsg = "系统升级失败" + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/system/netinfo',methods=['GET']) #获取系统信息 时已经获取,不再处理 +@login_required +async def system_net_info(): #获取网络信息 + return jsonify(1) + +@api.route('/system/changenet',methods=['POST']) +@login_required +async def system_net_change(): #修改网络信息 + ip = (await request.form)['ip'] + mask = (await request.form)['mask'] + gateway = (await request.form)['gateway'] + #对ip,mask.geteway的合法性进行判断 + if mReM.is_valid_ip(ip) and mReM.is_valid_ip(gateway) and mReM.is_valid_subnet_mask(mask): + strsql = f"update device set dev_ip='{ip}',dev_mask='{mask}',dev_gateway='{gateway}'" + if mDBM.do_sql(strsql): + reStatus = 1 + reMsg = '修改成功,请确认无误后,重启设备!' + else: + reStatus = 0 + reMsg = '修改失败,请联系技术支持!' + else: + reStatus = 0 + reMsg= '输入的参数不合法,请检查!' + return jsonify({'status':reStatus,'msg':reMsg}) + +@api.route('/system/changeserver',methods=['POST']) +@login_required +async def system_server_change(): #修改服务器信息 + ip = (await request.form)['ip'] + port = (await request.form)['port'] + # 对ip,mask.geteway的合法性进行判断 + if mReM.is_valid_ip(ip) and mReM.is_valid_port(port): + strsql = f"update device set service_ip='{ip}',service_port='{port}';" + if mDBM.do_sql(strsql): + reStatus = 1 + reMsg = '修改成功,请确认无误后,重启设备!' + else: + reStatus = 0 + reMsg = '修改失败,请联系技术支持!' + else: + reStatus = 0 + reMsg = '输入的参数不合法,请检查!' + return jsonify({'status': reStatus, 'msg': reMsg}) diff --git a/web/API/user.py b/web/API/user.py new file mode 100644 index 0000000..0995809 --- /dev/null +++ b/web/API/user.py @@ -0,0 +1,114 @@ +import os +from quart import Quart, render_template, request, session, redirect, url_for,jsonify,send_file +from quart_sqlalchemy import SQLAlchemy +from quart_session import Session +from web.common.utils import generate_captcha,login_required +from myutils.ConfigManager import myCongif +from core.DBManager import mDBM +from . import api +from web.common.errors import handle_error + + +@api.route('/user/code',methods=['GET']) +async def user_get_code(): #获取验证码 + captcha_text, buffer = generate_captcha() + print(captcha_text) + session['captcha'] = captcha_text # 记录验证码? + return await send_file(buffer, mimetype='image/png') + + +@api.route('/user/login',methods=['POST']) +async def user_login(): #用户登录 + username = (await request.form)['username'] + password = (await request.form)['password'] + captcha = (await request.form)['captcha'] + if captcha != session.get('captcha'): + #验证码验证过后,需要失效 + #? + return '验证码错误', 400 + #比对用户名和密码 + strsql = f"select password from user where username = '{username}'" + db_password = mDBM.do_select(strsql,1) + if db_password: + if db_password[0] == password: #后续需要对密码进行MD5加默 + print("登录成功") + session['user'] = username + return redirect(url_for('main.get_html', html='实时预览.html')) + return '用户名或密码错误', 400 + +@api.route('/user/userinfo',methods=['GET']) +@login_required +async def user_info(): #获取用户列表 + strsql = "select username,status,people,tellnum from user;"; + data = mDBM.do_select(strsql) + if data: + user_list = [{"username": user[0], "status": user[1], + "people":user[2],"tellnum":user[3]} for user in data] + return jsonify(user_list) + else: + return jsonify(0) + + +@api.route('/user/adduser',methods=['POST']) +@login_required +async def user_adduser(): #新增用户 + username = (await request.form)['username'] + people = (await request.form)['people'] + tellnum = (await request.form)['tellnum'] + strsql = f"select username from user where username = '{username}';" + data = mDBM.do_select(strsql) + if data: + reStatus = 0 + reMsg = '用户名重复,请重新输入!' + else: + strsql = (f"INSERT INTO user (username ,password ,status,people,tellnum ) VALUES " + f"('{username}','{myCongif.get_data("pw")}',1,'{people}','{tellnum}');") + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '添加用户成功' + else: + reStatus = 0 + reMsg = '添加用户异常,请联系管理员处理!' + return jsonify({'status':reStatus,'msg':reMsg}) + +@api.route('/user/passwd',methods=['POST']) +@login_required +async def user_change_passwd(): #重置密码 + username = (await request.form)['username'] + strsql = f"update user set password='{myCongif.get_data("pw")}' where username='{username}';" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '重置密码成功' + else: + reStatus = 0 + reMsg = '重置密码失败,请联系管理员处理!' + return jsonify({'status':reStatus,'msg':reMsg}) + +@api.route('/user/changeuser',methods=['POST']) +@login_required +async def user_change_user_info(): #修改用户信息 + username = (await request.form)['username'] + people = (await request.form)['people'] + tellnum = (await request.form)['tellnum'] + strsql = f"update user set people='{people}',tellnum='{tellnum}' where username='{username}';" + ret = mDBM.do_sql(strsql) + if ret == True: + reStatus = 1 + reMsg = '修改用户信息成功' + else: + reStatus = 0 + reMsg = '修改失败,请联系管理员处理!' + return jsonify({'status': reStatus, 'msg': reMsg}) + +@api.route('/user/', methods=['GET']) +async def get_user(user_id): + try: + user = user_id + if user: + return jsonify(user) + else: + return jsonify({'error': 'User not found'}), 404 + except Exception as e: + return handle_error(e) \ No newline at end of file diff --git a/web/API/viedo.py b/web/API/viedo.py new file mode 100644 index 0000000..974368e --- /dev/null +++ b/web/API/viedo.py @@ -0,0 +1,312 @@ +import cv2 +import asyncio +import av +import torch +import json +import time +import os +import sys +import numpy as np +from . import api +from quart import jsonify, request,render_template, Response,websocket +from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack +from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRecorder +from fractions import Fraction +from shapely.geometry import Point, Polygon +import threading +import logging + +# 配置日志 +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) +rtsp_url = 'rtsp://127.0.0.1/live1' +#-------------------基于WEBRTC实现拉流 +pcs = set() #创建一个空的集合,去重复且无序 + +''' +--------------基础信息--后续封装在函数里--------------- +''' +#print(f"Current working directory (video.py): {os.getcwd()}") +# 自定义模型文件的路径 +model_path = 'model/mode_test/yolov5s.pt' # 假设模型文件名为 yolov5s.pt 并与执行文件在同一个目录 +# 本地YOLOv5仓库路径 +repo_path = 'model/base_model/yolov5' + +# 加载自定义模型 +model = torch.hub.load(repo_path, 'custom', path=model_path, source='local') +# 定义监控区域(多边形顶点) +region_points = [(100, 100), (500, 100), (500, 400), (100, 400)] +polygon = Polygon(region_points) + +# 打开摄像头 +def get_camera(): + cap = cv2.VideoCapture(0) + if not cap.isOpened(): + raise IOError("Cannot open webcam") + return cap + +# cap = get_camera() +''' +---------------------传输-------------------------- +''' +class VideoTransformTrack(VideoStreamTrack): + kind = "video" + def __init__(self,strUrl,itype=1): #0-usb,1-RTSP,2-海康SDK + super().__init__() + self.cap = cv2.VideoCapture(strUrl) + if not self.cap.isOpened(): + raise Exception("Unable to open video source") + # Set desired resolution and frame rate + # self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # Set width to 1280 + # self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # Set height to 720 + # self.cap.set(cv2.CAP_PROP_FPS, 30) # Set frame rate to 30 FPS + self.frame_rate = int(self.cap.get(cv2.CAP_PROP_FPS)) or 15 # Default to 30 if unable to get FPS + self.time_base = Fraction(1, self.frame_rate) + + async def recv(self): + start_time = time.time() # 开始时间 + ret, frame = self.cap.read() + if not ret: + raise Exception("Unable to read frame") + # # Convert frame to RGB + # frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + # + # # Convert to VideoFrame + # video_frame = av.VideoFrame.from_ndarray(frame_rgb, format="rgb24") + # # Set timestamp + # video_frame.pts = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES)) + # video_frame.time_base = self.time_base + + img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + #img = frame.to_ndarray(format="bgr24") + # 使用 Yolov5 进行目标检测 + results = model(img) + detections = results.pandas().xyxy[0].to_dict(orient="records") + # 绘制检测结果 + for det in detections: + if det['name'] == 'person': # 只检测人员 + x1, y1, x2, y2 = int(det['xmin']), int(det['ymin']), int(det['xmax']), int(det['ymax']) + cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) + cv2.putText(img, f"{det['name']} {det['confidence']:.2f}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, + (0, 255, 0), 2) + # 将检测结果图像转换为帧 + new_frame = av.VideoFrame.from_ndarray(img, format="rgb24") + new_frame.pts = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES)) + new_frame.time_base = self.time_base + end_time = time.time() # 结束时间 + print(f"Processing time: {end_time - start_time} seconds") + return new_frame + +@api.route('/offer', methods=['POST']) +async def offer(): + #接收客户端的连接请求 + params = await request.json + url_type = params["url_type"] + url = params["url"] + #提取客户端发来的SDP,生成服务器端的SDP + offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) + # # 配置 STUN 服务器 + # ice_servers = [{"urls": ["stun:stun.voipbuster.com"]}] + # pc = RTCPeerConnection(configuration={"iceServers": ice_servers}) + pc = RTCPeerConnection() #实例化一个rtcp对象 + pcs.add(pc) #集合中添加一个对象,若已存在则不添加 + + @pc.on("datachannel") + def on_datachannel(channel): + @channel.on("message") + def on_message(message): + print(f'Message received: {message}') + # 处理接收到的数据 + if message == 'pause': + # 执行暂停操作 + pass + elif message == 'resume': + # 执行继续操作 + pass + #监听RTC连接状态 + @pc.on("iconnectionstatechange") #当ice连接状态发生变化时 + async def iconnectionstatechange(): + if pc.iceConnectionState == "failed": + await pc.close() + pcs.discard(pc) #移除对象 + + # 添加视频轨道 + video_track = VideoTransformTrack(url,url_type) + pc.addTrack(video_track) + + # @pc.on('track') --- stream.getTracks().forEach(track => pc.addTrack(track, stream)); 猜测是这里触发的添加了摄像头通道 + # def on_track(track): + # if track.kind == 'video': + # local_video = VideoTransformTrack(track) + # pc.addTrack(local_video) + + # 记录客户端 SDP + await pc.setRemoteDescription(offer) + # 生成本地 SDP + answer = await pc.createAnswer() + # 记录本地 SDP + await pc.setLocalDescription(answer) + + print("返回sdp") + return jsonify({ + "sdp": pc.localDescription.sdp, + "type": pc.localDescription.type + }) + +@api.route('/shutdown', methods=['POST']) +async def shutdown(): + coros = [pc.close() for pc in pcs] + await asyncio.gather(*coros) + pcs.clear() + return 'Server shutting down...' + +''' +----------------------实现方式二,网页显示分析画面--基础版 +''' + +def is_point_in_polygon(point, polygon): + return polygon.contains(Point(point)) + +frame_buffer = [] +def camera_thread(): + global cap, frame_buffer + while True: + try: + ret, frame = cap.read() + if not ret: + logger.warning("Camera disconnected. Reconnecting...") + cap.release() + cap = get_camera() + continue + frame = cv2.resize(frame, (640, 480)) # 降低视频分辨率 + frame_buffer.append(frame) + if len(frame_buffer) > 10: # 保持缓冲区大小不超过10帧 + frame_buffer.pop(0) + logger.debug("drop one frame!") + except Exception as e: + logger.error(f"Error in camera thread: {e}") + continue + +#threading.Thread(target=camera_thread, daemon=True).start() + +async def generate_frames(): + global frame_buffer + while True: + frame = None + buffer = None + try: + if frame_buffer: + frame = frame_buffer.pop(0) + + # 进行推理 + results = model(frame) + detections = results.pandas().xyxy[0] + + # 绘制监控区域 + cv2.polylines(frame, [np.array(region_points, np.int32)], isClosed=True, color=(0, 255, 0), thickness=2) + + for _, row in detections.iterrows(): + if row['name'] == 'person': + # 获取人员检测框的中心点 + x_center = (row['xmin'] + row['xmax']) / 2 + y_center = (row['ymin'] + row['ymax']) / 2 + + if is_point_in_polygon((x_center, y_center), polygon): + # 触发报警 + cv2.putText(frame, 'Alert: Intrusion Detected', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, + (0, 0, 255), 2) + #logger.info('Alert: Intrusion Detected') + + # 绘制检测框 + cv2.rectangle(frame, (int(row['xmin']), int(row['ymin'])), (int(row['xmax']), int(row['ymax'])), + (255, 0, 0), 2) + cv2.circle(frame, (int(x_center), int(y_center)), 5, (0, 0, 255), -1) + + ret, buffer = cv2.imencode('.jpg', frame) + if not ret: + continue + frame_bytes = buffer.tobytes() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n') + await asyncio.sleep(0.2) + except Exception as e: + logger.error(f"Error in generate_frames: {e}") + finally: + if frame is not None: + del frame + if buffer is not None: + del buffer + +@api.route('/video_feed') +async def video_feed(): + #return jsonify(1) + return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') + +''' +----------------------实现方式三,网页显示分析画面--WEBRTC +''' + + +class VideoTransformTrack_new(VideoStreamTrack): + def __init__(self, track): + super().__init__() + self.track = track + + async def recv(self): + frame = await self.track.recv() + img = frame.to_ndarray(format="bgr24") + + # 使用 Yolov5 进行目标检测 + results = model(img) + detections = results.pandas().xyxy[0].to_dict(orient="records") + + # 绘制检测结果 + for det in detections: + if det['name'] == 'person': # 只检测人员 + x1, y1, x2, y2 = int(det['xmin']), int(det['ymin']), int(det['xmax']), int(det['ymax']) + cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) + cv2.putText(img, f"{det['name']} {det['confidence']:.2f}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, + (0, 255, 0), 2) + + # 将检测结果图像转换为帧 + new_frame = frame.from_ndarray(img, format="bgr24") + return new_frame + +pcs = set() + + +@api.websocket('/ws') +async def ws(): + params = await websocket.receive_json() + offer = RTCSessionDescription(sdp=params['sdp'], type=params['type']) + + pc = RTCPeerConnection() + pcs.add(pc) + + @pc.on('iceconnectionstatechange') + async def on_iceconnectionstatechange(): + if pc.iceConnectionState == 'failed': + await pc.close() + pcs.discard(pc) + + @pc.on('track') + def on_track(track): + if track.kind == 'video': + local_video = VideoTransformTrack(track) + pc.addTrack(local_video) + + await pc.setRemoteDescription(offer) + answer = await pc.createAnswer() + await pc.setLocalDescription(answer) + + await websocket.send(json.dumps({ + 'sdp': pc.localDescription.sdp, + 'type': pc.localDescription.type + })) + + try: + while True: + await asyncio.sleep(1) + finally: + await pc.close() + pcs.discard(pc) \ No newline at end of file diff --git a/web/__init__.py b/web/__init__.py index 0691204..89e81ca 100644 --- a/web/__init__.py +++ b/web/__init__.py @@ -1,213 +1,47 @@ -import os -import sqlite3 -import cv2 -import asyncio -import av -from aiohttp import web -from quart import Quart, render_template, request, jsonify,send_from_directory -from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack, RTCConfiguration, RTCIceServer -from fractions import Fraction -from .main import main as main_blueprint +from quart import Quart, render_template, request, jsonify,send_from_directory,session,redirect, url_for +from quart_session import Session +from .main import main +from .API import api +from functools import wraps + +from quart_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + #app.config['SECRET_KEY'] = 'mysecret' #密钥 --需要放配置文件 #socketio = SocketIO(app) -''' -************************ -功能函数 -''' -rtsp_url = 'rtsp://127.0.0.1/live1' -#-------------------基于WEBRTC实现拉流 -pcs = set() #创建一个空的集合,去重复且无序 - def create_app(): app = Quart(__name__) - # 注册蓝图 - app.register_blueprint(main_blueprint) + #相关配置--设置各种配置选项,这些选项会在整个应用程序中被访问和使用。 + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + app.config['SECRET_KEY'] = 'zfxxkj_2024_!@#' + + app.config['SESSION_TYPE'] = 'redis' # session类型 + app.config['SESSION_FILE_DIR'] = './sessions' # session保存路径 + app.config['SESSION_PERMANENT'] = True # 如果设置为True,则关闭浏览器session就失效。 + app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密 + Session(app) + + #ORM数据库管理 + # db = SQLAlchemy() + # migrate = Migrate() + # db.init_app(app) + # migrate.init_app(app, db) # 创建对象,使用它来将ORM模型映射到数据库 -- 效果是及时更新数据的修改到文档? + + # 注册main + app.register_blueprint(main) + #注册API模块 + app.register_blueprint(api,url_prefix = '/api') return app -class VideoTransformTrack(VideoStreamTrack): - kind = "video" - - def __init__(self): - super().__init__() - self.cap = cv2.VideoCapture('rtsp://127.0.0.1/live1') - if not self.cap.isOpened(): - raise Exception("Unable to open video source") - # Set desired resolution and frame rate - # self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # Set width to 1280 - # self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # Set height to 720 - # self.cap.set(cv2.CAP_PROP_FPS, 30) # Set frame rate to 30 FPS - self.frame_rate = int(self.cap.get(cv2.CAP_PROP_FPS)) or 30 # Default to 30 if unable to get FPS - self.time_base = Fraction(1, self.frame_rate) - - async def recv(self): - ret, frame = self.cap.read() - if not ret: - raise Exception("Unable to read frame") - # Convert frame to RGB - frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - # Convert to VideoFrame - video_frame = av.VideoFrame.from_ndarray(frame_rgb, format="rgb24") - # Set timestamp - video_frame.pts = int(self.cap.get(cv2.CAP_PROP_POS_FRAMES)) - video_frame.time_base = self.time_base - return video_frame - -''' -************************ -页面路由 -''' -@app.route('/') -async def index(): - #return await render_template('登录.html') - return await render_template('登录.html') - -@app.route('/favicon.ico') -async def favicon(): - return await send_from_directory('static', 'favicon.ico') - -@app.route('/') -async def get_html(html): - return await render_template(html) - -#--------------------webrtc----------------- -async def server(pc, offer): - # 监听 RTC 连接状态 - @pc.on("connectionstatechange") - async def on_connectionstatechange(): - print("Connection state is %s" % pc.connectionState) - # 当 RTC 连接中断后将连接关闭 - if pc.connectionState == "failed": - await pc.close() - pcs.discard(pc) - - # 监听客户端发来的视频流 - @pc.on("track") - def on_track(track): - print("======= received track: ", track) - if track.kind == "video": - # # 对视频流进行人脸替换 - # t = FaceSwapper(track) - # 绑定替换后的视频流 - pc.addTrack(track) - - # 记录客户端 SDP - await pc.setRemoteDescription(offer) - # 生成本地 SDP - answer = await pc.createAnswer() - # 记录本地 SDP - await pc.setLocalDescription(answer) - - -@app.route('/offer', methods=['POST']) -async def offer(): - #接收客户端的连接请求 - params = await request.json - #params = await request.json() - print(params) - #提取客户端发来的SDP,生成服务器端的SDP - offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) - # Configure ICE servers if necessary - #config = RTCConfiguration([RTCIceServer(urls=['stun:stun.voiparound.com'])]) - pc = RTCPeerConnection() #实例化一个rtcp对象 - pcs.add(pc) #集合中添加一个对象,若已存在则不添加 - print(pc) - @pc.on("datachannel") - def on_datachannel(channel): - @channel.on("message") - def on_message(message): - if isinstance(message, str) and message.startswith("ping"): - channel.send("pong" + message[4:]) - - #监听RTC连接状态 - @pc.on("iconnectionstatechange") #当ice连接状态发生变化时 - async def iconnectionstatechange(): - if pc.iceConnectionState == "failed": - await pc.close() - pcs.discard(pc) #移除对象 - - # 添加视频轨道 - video_track = VideoTransformTrack() - pc.addTrack(video_track) - print("完成视频轨道的添加") - # 记录客户端 SDP - await pc.setRemoteDescription(offer) - # 生成本地 SDP - answer = await pc.createAnswer() - # 记录本地 SDP - await pc.setLocalDescription(answer) - - return jsonify({ - "sdp": pc.localDescription.sdp, - "type": pc.localDescription.type - }) - -@app.route('/shutdown', methods=['POST']) -async def shutdown(): - coros = [pc.close() for pc in pcs] - await asyncio.gather(*coros) - pcs.clear() - return 'Server shutting down...' - -'''3333 -各种配置文件路由 -''' -@app.route('/data/') -async def data(file): - return await send_from_directory('static/data',file) - -@app.route('/files//') -async def files(subdir,file): - return await send_from_directory(os.path.join('static/files', subdir),file) - -@app.route('/images//') -async def images(subdir,file): - return await send_from_directory(os.path.join('static/images', subdir),file) - -@app.route('/resources/') -async def resources(file): - return await send_from_directory('static/resources',file) - -@app.route('/resources//') -async def resources_dir(subdir,file): - return await send_from_directory(os.path.join('static/resources', subdir),file) - -@app.route('/resources/css//') -async def resources_css_dir(subdir,file): - return await send_from_directory(os.path.join('static/resources/css', subdir),file) - -@app.route('/resources/scripts//') -async def resources_scripts_dir(subdir,file): - return await send_from_directory(os.path.join('static/resources/scripts', subdir),file) - -''' -后端数据接口 -''' -@app.route('/submit', methods=['POST']) -def submit(): - if request.method == 'POST': - suggestion = request.form['suggestion'] - file = request.files['attachment'] - - if file: - filename = file.filename - file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) - - # 将意见和附件的文件名保存到数据库 - conn = sqlite3.connect(DB_NAME) - cursor = conn.cursor() - cursor.execute('INSERT INTO suggestions (suggestion, attachment_filename) VALUES (?, ?)', - (suggestion, filename)) - conn.commit() - conn.close() - - return "Thanks for your suggestion and attachment! Data saved to the database." - - # 如果没有附件上传的逻辑处理 - return "Data saved!" +def login_required(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + if 'user' not in session: + return redirect(url_for('main.login')) + return await f(*args, **kwargs) + return decorated_function -if __name__ == '__main__': - #app.run(debug=True) - app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/ViewManager.py b/web/common/__init__.py similarity index 100% rename from ViewManager.py rename to web/common/__init__.py diff --git a/web/common/errors.py b/web/common/errors.py new file mode 100644 index 0000000..7cf2cf5 --- /dev/null +++ b/web/common/errors.py @@ -0,0 +1,4 @@ +from quart import jsonify + +def handle_error(e): + return jsonify({'error':str(e)}),500 \ No newline at end of file diff --git a/web/common/models.py b/web/common/models.py new file mode 100644 index 0000000..c446c76 --- /dev/null +++ b/web/common/models.py @@ -0,0 +1,10 @@ +from app import db +import datetime + +class Captcha(db.Model): + id = db.Column(db.Integer, primary_key=True) + captcha_text = db.Column(db.String(10), nullable=False) + timestamp = db.Column(db.DateTime, default=datetime.datetime.utcnow) + + def __init__(self, captcha_text): + self.captcha_text = captcha_text diff --git a/web/common/utils.py b/web/common/utils.py new file mode 100644 index 0000000..68947f6 --- /dev/null +++ b/web/common/utils.py @@ -0,0 +1,44 @@ +from PIL import Image, ImageDraw, ImageFont,ImageFilter +import random +import string +import os +import io +from functools import wraps +from quart import session, redirect, url_for + +def generate_captcha(): + characters = string.ascii_uppercase + string.digits + captcha_text = ''.join(random.choices(characters, k=6)) + + font = ImageFont.truetype("arial.ttf", 36) + image = Image.new('RGB', (200, 60), color=(255, 255, 255)) + draw = ImageDraw.Draw(image) + + for i in range(6): + draw.text((10 + i * 30, 10), captcha_text[i], font=font, + fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))) + # 模糊化处理 + image = image.filter(ImageFilter.BLUR) + + # 将图片保存到BytesIO流中 + buffer = io.BytesIO() + image.save(buffer, 'jpeg') + buffer.seek(0) + + # captcha_path = os.path.join('static', 'captcha', f'{captcha_text}.png') + # image.save(captcha_path) + # return captcha_text, f'static/captcha/{captcha_text}.png' + return captcha_text, buffer + + +def verify_captcha(user_input, actual_captcha): + return user_input == actual_captcha + + +def login_required(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + if 'user' not in session: + return redirect(url_for('main.index',error='未登录,请重新登录')) + return await f(*args, **kwargs) + return decorated_function diff --git a/web/main/__init__.py b/web/main/__init__.py index 08671eb..3d5df65 100644 --- a/web/main/__init__.py +++ b/web/main/__init__.py @@ -1,5 +1,5 @@ from quart import Blueprint -main = Blueprint('main', __name__) +main = Blueprint('main', __name__,template_folder='templates') from . import routes diff --git a/web/main/routes.py b/web/main/routes.py index 36a3e05..d5025b4 100644 --- a/web/main/routes.py +++ b/web/main/routes.py @@ -1,10 +1,92 @@ -from quart import render_template -from . import main +import os +from web.main import main +from quart import render_template, send_from_directory,request +from quart import session, redirect, url_for,flash +from functools import wraps +from core.Upload_file import allowed_file +from myutils.ConfigManager import myCongif +from werkzeug.utils import secure_filename + +''' +页面路由 +''' + +def login_required(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + if 'user' not in session: + return redirect(url_for('main.index',error='未登录,请重新登录')) + return await f(*args, **kwargs) + return decorated_function @main.route('/') async def index(): - return await render_template('index.html') + # error = request.args.get('error') + return await render_template('实时预览.html') + # return await render_template('登录.html',error=error) + #return await render_template('index_webrtc.html') + +# @main.route('/', methods=['GET', 'POST']) +# async def upload_file(): +# if request.method == 'POST': +# form = await request.form +# files = await request.files +# if 'file' not in files: +# flash('No file part') +# return redirect(request.url) +# file = files['file'] +# if file.filename == '': +# flash('No selected file') +# return redirect(request.url) +# if file and allowed_file(file.filename): +# print("上传文件") +# filename = secure_filename(file.filename) +# file_path = os.path.join(myCongif.get_data('UPLOAD_FOLDER'), filename) +# await file.save(file_path) +# flash('File successfully uploaded') +# return redirect(url_for('main.upload_file')) +# else: +# flash('Allowed file types are zip') +# return redirect(request.url) +# return await render_template('upload.html') + +@main.route('/favicon.ico') +async def favicon(): + return await send_from_directory('web/main/static', 'favicon.ico') + +@main.route('/') +@login_required +async def get_html(html): + return await render_template(html) + + +''' +各种配置文件路由 +''' +@main.route('/data/') +async def data(file): + return await send_from_directory('web/main/static/data', file) + +@main.route('/files//') +async def files(subdir,file): + return await send_from_directory(os.path.join('web/main/static/files', subdir), file) + +@main.route('/images//') +async def images(subdir,file): + return await send_from_directory(os.path.join('web/main/static/images', subdir), file) + +@main.route('/resources/') +async def resources(file): + return await send_from_directory('web/main/static/resources', file) + +@main.route('/resources//') +async def resources_dir(subdir,file): + return await send_from_directory(os.path.join('web/main/static/resources', subdir), file) + +@main.route('/resources/css//') +async def resources_css_dir(subdir,file): + return await send_from_directory(os.path.join('web/main/static/resources/css', subdir), file) -@main.route('/about') -async def about(): - return await render_template('about.html') +@main.route('/resources/scripts//') +async def resources_scripts_dir(subdir,file): + return await send_from_directory(os.path.join('web/main/static/resources/scripts', subdir), file) diff --git a/web/static/data/document.js b/web/main/static/data/document.js similarity index 100% rename from web/static/data/document.js rename to web/main/static/data/document.js diff --git a/web/static/data/styles.css b/web/main/static/data/styles.css similarity index 100% rename from web/static/data/styles.css rename to web/main/static/data/styles.css diff --git a/web/static/favicon.ico b/web/main/static/favicon.ico similarity index 100% rename from web/static/favicon.ico rename to web/main/static/favicon.ico diff --git a/web/static/files/实时预览/data.js b/web/main/static/files/实时预览/data.js similarity index 100% rename from web/static/files/实时预览/data.js rename to web/main/static/files/实时预览/data.js diff --git a/web/static/files/实时预览/styles.css b/web/main/static/files/实时预览/styles.css similarity index 100% rename from web/static/files/实时预览/styles.css rename to web/main/static/files/实时预览/styles.css diff --git a/web/static/files/用户管理/data.js b/web/main/static/files/用户管理/data.js similarity index 100% rename from web/static/files/用户管理/data.js rename to web/main/static/files/用户管理/data.js diff --git a/web/static/files/用户管理/styles.css b/web/main/static/files/用户管理/styles.css similarity index 100% rename from web/static/files/用户管理/styles.css rename to web/main/static/files/用户管理/styles.css diff --git a/web/static/files/登录/data.js b/web/main/static/files/登录/data.js similarity index 100% rename from web/static/files/登录/data.js rename to web/main/static/files/登录/data.js diff --git a/web/static/files/登录/styles.css b/web/main/static/files/登录/styles.css similarity index 100% rename from web/static/files/登录/styles.css rename to web/main/static/files/登录/styles.css diff --git a/web/static/files/算法管理/data.js b/web/main/static/files/算法管理/data.js similarity index 100% rename from web/static/files/算法管理/data.js rename to web/main/static/files/算法管理/data.js diff --git a/web/static/files/算法管理/styles.css b/web/main/static/files/算法管理/styles.css similarity index 100% rename from web/static/files/算法管理/styles.css rename to web/main/static/files/算法管理/styles.css diff --git a/web/static/files/系统管理/data.js b/web/main/static/files/系统管理/data.js similarity index 100% rename from web/static/files/系统管理/data.js rename to web/main/static/files/系统管理/data.js diff --git a/web/static/files/系统管理/styles.css b/web/main/static/files/系统管理/styles.css similarity index 100% rename from web/static/files/系统管理/styles.css rename to web/main/static/files/系统管理/styles.css diff --git a/web/static/files/通道管理/data.js b/web/main/static/files/通道管理/data.js similarity index 100% rename from web/static/files/通道管理/data.js rename to web/main/static/files/通道管理/data.js diff --git a/web/static/files/通道管理/styles.css b/web/main/static/files/通道管理/styles.css similarity index 100% rename from web/static/files/通道管理/styles.css rename to web/main/static/files/通道管理/styles.css diff --git a/web/static/images/实时预览/u20_menu.png b/web/main/static/images/实时预览/u20_menu.png similarity index 100% rename from web/static/images/实时预览/u20_menu.png rename to web/main/static/images/实时预览/u20_menu.png diff --git a/web/static/images/实时预览/u22.png b/web/main/static/images/实时预览/u22.png similarity index 100% rename from web/static/images/实时预览/u22.png rename to web/main/static/images/实时预览/u22.png diff --git a/web/static/images/实时预览/u26.png b/web/main/static/images/实时预览/u26.png similarity index 100% rename from web/static/images/实时预览/u26.png rename to web/main/static/images/实时预览/u26.png diff --git a/web/static/images/实时预览/u32.png b/web/main/static/images/实时预览/u32.png similarity index 100% rename from web/static/images/实时预览/u32.png rename to web/main/static/images/实时预览/u32.png diff --git a/web/static/images/实时预览/u48.png b/web/main/static/images/实时预览/u48.png similarity index 100% rename from web/static/images/实时预览/u48.png rename to web/main/static/images/实时预览/u48.png diff --git a/web/static/images/实时预览/u48_selected.png b/web/main/static/images/实时预览/u48_selected.png similarity index 100% rename from web/static/images/实时预览/u48_selected.png rename to web/main/static/images/实时预览/u48_selected.png diff --git a/web/static/images/用户管理/u410.png b/web/main/static/images/用户管理/u410.png similarity index 100% rename from web/static/images/用户管理/u410.png rename to web/main/static/images/用户管理/u410.png diff --git a/web/static/images/用户管理/u411.png b/web/main/static/images/用户管理/u411.png similarity index 100% rename from web/static/images/用户管理/u411.png rename to web/main/static/images/用户管理/u411.png diff --git a/web/static/images/用户管理/u412.png b/web/main/static/images/用户管理/u412.png similarity index 100% rename from web/static/images/用户管理/u412.png rename to web/main/static/images/用户管理/u412.png diff --git a/web/static/images/用户管理/u413.png b/web/main/static/images/用户管理/u413.png similarity index 100% rename from web/static/images/用户管理/u413.png rename to web/main/static/images/用户管理/u413.png diff --git a/web/static/images/用户管理/u441.png b/web/main/static/images/用户管理/u441.png similarity index 100% rename from web/static/images/用户管理/u441.png rename to web/main/static/images/用户管理/u441.png diff --git a/web/static/images/用户管理/u442.png b/web/main/static/images/用户管理/u442.png similarity index 100% rename from web/static/images/用户管理/u442.png rename to web/main/static/images/用户管理/u442.png diff --git a/web/static/images/用户管理/u443.png b/web/main/static/images/用户管理/u443.png similarity index 100% rename from web/static/images/用户管理/u443.png rename to web/main/static/images/用户管理/u443.png diff --git a/web/static/images/用户管理/u444.png b/web/main/static/images/用户管理/u444.png similarity index 100% rename from web/static/images/用户管理/u444.png rename to web/main/static/images/用户管理/u444.png diff --git a/web/static/images/用户管理/u466.png b/web/main/static/images/用户管理/u466.png similarity index 100% rename from web/static/images/用户管理/u466.png rename to web/main/static/images/用户管理/u466.png diff --git a/web/static/images/用户管理/u467.png b/web/main/static/images/用户管理/u467.png similarity index 100% rename from web/static/images/用户管理/u467.png rename to web/main/static/images/用户管理/u467.png diff --git a/web/static/images/用户管理/u468.png b/web/main/static/images/用户管理/u468.png similarity index 100% rename from web/static/images/用户管理/u468.png rename to web/main/static/images/用户管理/u468.png diff --git a/web/static/images/用户管理/u469.png b/web/main/static/images/用户管理/u469.png similarity index 100% rename from web/static/images/用户管理/u469.png rename to web/main/static/images/用户管理/u469.png diff --git a/web/static/images/用户管理/u497.png b/web/main/static/images/用户管理/u497.png similarity index 100% rename from web/static/images/用户管理/u497.png rename to web/main/static/images/用户管理/u497.png diff --git a/web/static/images/用户管理/u498.png b/web/main/static/images/用户管理/u498.png similarity index 100% rename from web/static/images/用户管理/u498.png rename to web/main/static/images/用户管理/u498.png diff --git a/web/static/images/用户管理/u499.png b/web/main/static/images/用户管理/u499.png similarity index 100% rename from web/static/images/用户管理/u499.png rename to web/main/static/images/用户管理/u499.png diff --git a/web/static/images/用户管理/u500.png b/web/main/static/images/用户管理/u500.png similarity index 100% rename from web/static/images/用户管理/u500.png rename to web/main/static/images/用户管理/u500.png diff --git a/web/static/images/登录/u12.png b/web/main/static/images/登录/u12.png similarity index 100% rename from web/static/images/登录/u12.png rename to web/main/static/images/登录/u12.png diff --git a/web/static/images/登录/u4.svg b/web/main/static/images/登录/u4.svg similarity index 100% rename from web/static/images/登录/u4.svg rename to web/main/static/images/登录/u4.svg diff --git a/web/static/images/算法管理/u252.png b/web/main/static/images/算法管理/u252.png similarity index 100% rename from web/static/images/算法管理/u252.png rename to web/main/static/images/算法管理/u252.png diff --git a/web/static/images/算法管理/u253.png b/web/main/static/images/算法管理/u253.png similarity index 100% rename from web/static/images/算法管理/u253.png rename to web/main/static/images/算法管理/u253.png diff --git a/web/static/images/算法管理/u254.png b/web/main/static/images/算法管理/u254.png similarity index 100% rename from web/static/images/算法管理/u254.png rename to web/main/static/images/算法管理/u254.png diff --git a/web/static/images/算法管理/u255.png b/web/main/static/images/算法管理/u255.png similarity index 100% rename from web/static/images/算法管理/u255.png rename to web/main/static/images/算法管理/u255.png diff --git a/web/static/images/算法管理/u256.png b/web/main/static/images/算法管理/u256.png similarity index 100% rename from web/static/images/算法管理/u256.png rename to web/main/static/images/算法管理/u256.png diff --git a/web/static/images/算法管理/u312.png b/web/main/static/images/算法管理/u312.png similarity index 100% rename from web/static/images/算法管理/u312.png rename to web/main/static/images/算法管理/u312.png diff --git a/web/static/images/算法管理/u313.png b/web/main/static/images/算法管理/u313.png similarity index 100% rename from web/static/images/算法管理/u313.png rename to web/main/static/images/算法管理/u313.png diff --git a/web/static/images/算法管理/u314.png b/web/main/static/images/算法管理/u314.png similarity index 100% rename from web/static/images/算法管理/u314.png rename to web/main/static/images/算法管理/u314.png diff --git a/web/static/images/算法管理/u315.png b/web/main/static/images/算法管理/u315.png similarity index 100% rename from web/static/images/算法管理/u315.png rename to web/main/static/images/算法管理/u315.png diff --git a/web/static/images/算法管理/u316.png b/web/main/static/images/算法管理/u316.png similarity index 100% rename from web/static/images/算法管理/u316.png rename to web/main/static/images/算法管理/u316.png diff --git a/web/static/images/算法管理/u363.svg b/web/main/static/images/算法管理/u363.svg similarity index 100% rename from web/static/images/算法管理/u363.svg rename to web/main/static/images/算法管理/u363.svg diff --git a/web/static/images/算法管理/u363_disabled.svg b/web/main/static/images/算法管理/u363_disabled.svg similarity index 100% rename from web/static/images/算法管理/u363_disabled.svg rename to web/main/static/images/算法管理/u363_disabled.svg diff --git a/web/static/images/算法管理/u363_selected.svg b/web/main/static/images/算法管理/u363_selected.svg similarity index 100% rename from web/static/images/算法管理/u363_selected.svg rename to web/main/static/images/算法管理/u363_selected.svg diff --git a/web/static/images/算法管理/u363_selectedDisabled.svg b/web/main/static/images/算法管理/u363_selectedDisabled.svg similarity index 100% rename from web/static/images/算法管理/u363_selectedDisabled.svg rename to web/main/static/images/算法管理/u363_selectedDisabled.svg diff --git a/web/static/images/算法管理/u364.svg b/web/main/static/images/算法管理/u364.svg similarity index 100% rename from web/static/images/算法管理/u364.svg rename to web/main/static/images/算法管理/u364.svg diff --git a/web/static/images/算法管理/u364_disabled.svg b/web/main/static/images/算法管理/u364_disabled.svg similarity index 100% rename from web/static/images/算法管理/u364_disabled.svg rename to web/main/static/images/算法管理/u364_disabled.svg diff --git a/web/static/images/算法管理/u364_selected.svg b/web/main/static/images/算法管理/u364_selected.svg similarity index 100% rename from web/static/images/算法管理/u364_selected.svg rename to web/main/static/images/算法管理/u364_selected.svg diff --git a/web/static/images/算法管理/u364_selectedDisabled.svg b/web/main/static/images/算法管理/u364_selectedDisabled.svg similarity index 100% rename from web/static/images/算法管理/u364_selectedDisabled.svg rename to web/main/static/images/算法管理/u364_selectedDisabled.svg diff --git a/web/static/images/算法管理/u366.svg b/web/main/static/images/算法管理/u366.svg similarity index 100% rename from web/static/images/算法管理/u366.svg rename to web/main/static/images/算法管理/u366.svg diff --git a/web/static/images/算法管理/u366_disabled.svg b/web/main/static/images/算法管理/u366_disabled.svg similarity index 100% rename from web/static/images/算法管理/u366_disabled.svg rename to web/main/static/images/算法管理/u366_disabled.svg diff --git a/web/static/images/算法管理/u366_selected.svg b/web/main/static/images/算法管理/u366_selected.svg similarity index 100% rename from web/static/images/算法管理/u366_selected.svg rename to web/main/static/images/算法管理/u366_selected.svg diff --git a/web/static/images/算法管理/u366_selectedDisabled.svg b/web/main/static/images/算法管理/u366_selectedDisabled.svg similarity index 100% rename from web/static/images/算法管理/u366_selectedDisabled.svg rename to web/main/static/images/算法管理/u366_selectedDisabled.svg diff --git a/web/static/images/算法管理/u367.svg b/web/main/static/images/算法管理/u367.svg similarity index 100% rename from web/static/images/算法管理/u367.svg rename to web/main/static/images/算法管理/u367.svg diff --git a/web/static/images/算法管理/u367_disabled.svg b/web/main/static/images/算法管理/u367_disabled.svg similarity index 100% rename from web/static/images/算法管理/u367_disabled.svg rename to web/main/static/images/算法管理/u367_disabled.svg diff --git a/web/static/images/算法管理/u367_selected.svg b/web/main/static/images/算法管理/u367_selected.svg similarity index 100% rename from web/static/images/算法管理/u367_selected.svg rename to web/main/static/images/算法管理/u367_selected.svg diff --git a/web/static/images/算法管理/u367_selectedDisabled.svg b/web/main/static/images/算法管理/u367_selectedDisabled.svg similarity index 100% rename from web/static/images/算法管理/u367_selectedDisabled.svg rename to web/main/static/images/算法管理/u367_selectedDisabled.svg diff --git a/web/static/images/系统管理/background_color_u375.svg b/web/main/static/images/系统管理/background_color_u375.svg similarity index 100% rename from web/static/images/系统管理/background_color_u375.svg rename to web/main/static/images/系统管理/background_color_u375.svg diff --git a/web/static/images/系统管理/background_color_u406.svg b/web/main/static/images/系统管理/background_color_u406.svg similarity index 100% rename from web/static/images/系统管理/background_color_u406.svg rename to web/main/static/images/系统管理/background_color_u406.svg diff --git a/web/static/images/系统管理/numeric_value_u377.svg b/web/main/static/images/系统管理/numeric_value_u377.svg similarity index 100% rename from web/static/images/系统管理/numeric_value_u377.svg rename to web/main/static/images/系统管理/numeric_value_u377.svg diff --git a/web/static/images/系统管理/numeric_value_u408.svg b/web/main/static/images/系统管理/numeric_value_u408.svg similarity index 100% rename from web/static/images/系统管理/numeric_value_u408.svg rename to web/main/static/images/系统管理/numeric_value_u408.svg diff --git a/web/static/images/系统管理/pie_chart_u376.svg b/web/main/static/images/系统管理/pie_chart_u376.svg similarity index 100% rename from web/static/images/系统管理/pie_chart_u376.svg rename to web/main/static/images/系统管理/pie_chart_u376.svg diff --git a/web/static/images/系统管理/pie_chart_u407.svg b/web/main/static/images/系统管理/pie_chart_u407.svg similarity index 100% rename from web/static/images/系统管理/pie_chart_u407.svg rename to web/main/static/images/系统管理/pie_chart_u407.svg diff --git a/web/static/images/通道管理/u179.png b/web/main/static/images/通道管理/u179.png similarity index 100% rename from web/static/images/通道管理/u179.png rename to web/main/static/images/通道管理/u179.png diff --git a/web/static/images/通道管理/u180.png b/web/main/static/images/通道管理/u180.png similarity index 100% rename from web/static/images/通道管理/u180.png rename to web/main/static/images/通道管理/u180.png diff --git a/web/static/images/通道管理/u181.png b/web/main/static/images/通道管理/u181.png similarity index 100% rename from web/static/images/通道管理/u181.png rename to web/main/static/images/通道管理/u181.png diff --git a/web/static/images/通道管理/u182.png b/web/main/static/images/通道管理/u182.png similarity index 100% rename from web/static/images/通道管理/u182.png rename to web/main/static/images/通道管理/u182.png diff --git a/web/static/images/通道管理/u183.png b/web/main/static/images/通道管理/u183.png similarity index 100% rename from web/static/images/通道管理/u183.png rename to web/main/static/images/通道管理/u183.png diff --git a/web/static/images/通道管理/u184.png b/web/main/static/images/通道管理/u184.png similarity index 100% rename from web/static/images/通道管理/u184.png rename to web/main/static/images/通道管理/u184.png diff --git a/web/static/images/通道管理/u185.png b/web/main/static/images/通道管理/u185.png similarity index 100% rename from web/static/images/通道管理/u185.png rename to web/main/static/images/通道管理/u185.png diff --git a/web/static/images/通道管理/u186.svg b/web/main/static/images/通道管理/u186.svg similarity index 100% rename from web/static/images/通道管理/u186.svg rename to web/main/static/images/通道管理/u186.svg diff --git a/web/static/images/通道管理/u207.svg b/web/main/static/images/通道管理/u207.svg similarity index 100% rename from web/static/images/通道管理/u207.svg rename to web/main/static/images/通道管理/u207.svg diff --git a/web/static/images/通道管理/u208.svg b/web/main/static/images/通道管理/u208.svg similarity index 100% rename from web/static/images/通道管理/u208.svg rename to web/main/static/images/通道管理/u208.svg diff --git a/web/static/images/通道管理/u213.svg b/web/main/static/images/通道管理/u213.svg similarity index 100% rename from web/static/images/通道管理/u213.svg rename to web/main/static/images/通道管理/u213.svg diff --git a/web/static/images/通道管理/u214.svg b/web/main/static/images/通道管理/u214.svg similarity index 100% rename from web/static/images/通道管理/u214.svg rename to web/main/static/images/通道管理/u214.svg diff --git a/web/static/images/通道管理/u217.svg b/web/main/static/images/通道管理/u217.svg similarity index 100% rename from web/static/images/通道管理/u217.svg rename to web/main/static/images/通道管理/u217.svg diff --git a/web/static/images/通道管理/u217_disabled.svg b/web/main/static/images/通道管理/u217_disabled.svg similarity index 100% rename from web/static/images/通道管理/u217_disabled.svg rename to web/main/static/images/通道管理/u217_disabled.svg diff --git a/web/static/images/通道管理/u217_selected.svg b/web/main/static/images/通道管理/u217_selected.svg similarity index 100% rename from web/static/images/通道管理/u217_selected.svg rename to web/main/static/images/通道管理/u217_selected.svg diff --git a/web/static/images/通道管理/u217_selectedDisabled.svg b/web/main/static/images/通道管理/u217_selectedDisabled.svg similarity index 100% rename from web/static/images/通道管理/u217_selectedDisabled.svg rename to web/main/static/images/通道管理/u217_selectedDisabled.svg diff --git a/web/static/images/通道管理/u218.svg b/web/main/static/images/通道管理/u218.svg similarity index 100% rename from web/static/images/通道管理/u218.svg rename to web/main/static/images/通道管理/u218.svg diff --git a/web/static/images/通道管理/u218_disabled.svg b/web/main/static/images/通道管理/u218_disabled.svg similarity index 100% rename from web/static/images/通道管理/u218_disabled.svg rename to web/main/static/images/通道管理/u218_disabled.svg diff --git a/web/static/images/通道管理/u218_selected.svg b/web/main/static/images/通道管理/u218_selected.svg similarity index 100% rename from web/static/images/通道管理/u218_selected.svg rename to web/main/static/images/通道管理/u218_selected.svg diff --git a/web/static/images/通道管理/u218_selectedDisabled.svg b/web/main/static/images/通道管理/u218_selectedDisabled.svg similarity index 100% rename from web/static/images/通道管理/u218_selectedDisabled.svg rename to web/main/static/images/通道管理/u218_selectedDisabled.svg diff --git a/web/static/images/通道管理/u219.svg b/web/main/static/images/通道管理/u219.svg similarity index 100% rename from web/static/images/通道管理/u219.svg rename to web/main/static/images/通道管理/u219.svg diff --git a/web/static/images/通道管理/u219_disabled.svg b/web/main/static/images/通道管理/u219_disabled.svg similarity index 100% rename from web/static/images/通道管理/u219_disabled.svg rename to web/main/static/images/通道管理/u219_disabled.svg diff --git a/web/static/images/通道管理/u219_selected.svg b/web/main/static/images/通道管理/u219_selected.svg similarity index 100% rename from web/static/images/通道管理/u219_selected.svg rename to web/main/static/images/通道管理/u219_selected.svg diff --git a/web/static/images/通道管理/u219_selectedDisabled.svg b/web/main/static/images/通道管理/u219_selectedDisabled.svg similarity index 100% rename from web/static/images/通道管理/u219_selectedDisabled.svg rename to web/main/static/images/通道管理/u219_selectedDisabled.svg diff --git a/web/static/images/通道管理/u221.png b/web/main/static/images/通道管理/u221.png similarity index 100% rename from web/static/images/通道管理/u221.png rename to web/main/static/images/通道管理/u221.png diff --git a/web/static/images/通道管理/u222.png b/web/main/static/images/通道管理/u222.png similarity index 100% rename from web/static/images/通道管理/u222.png rename to web/main/static/images/通道管理/u222.png diff --git a/web/static/images/通道管理/u81.png b/web/main/static/images/通道管理/u81.png similarity index 100% rename from web/static/images/通道管理/u81.png rename to web/main/static/images/通道管理/u81.png diff --git a/web/static/images/通道管理/u82.png b/web/main/static/images/通道管理/u82.png similarity index 100% rename from web/static/images/通道管理/u82.png rename to web/main/static/images/通道管理/u82.png diff --git a/web/static/images/通道管理/u83.png b/web/main/static/images/通道管理/u83.png similarity index 100% rename from web/static/images/通道管理/u83.png rename to web/main/static/images/通道管理/u83.png diff --git a/web/static/images/通道管理/u84.png b/web/main/static/images/通道管理/u84.png similarity index 100% rename from web/static/images/通道管理/u84.png rename to web/main/static/images/通道管理/u84.png diff --git a/web/static/images/通道管理/u85.png b/web/main/static/images/通道管理/u85.png similarity index 100% rename from web/static/images/通道管理/u85.png rename to web/main/static/images/通道管理/u85.png diff --git a/web/static/images/通道管理/u86.png b/web/main/static/images/通道管理/u86.png similarity index 100% rename from web/static/images/通道管理/u86.png rename to web/main/static/images/通道管理/u86.png diff --git a/web/static/images/通道管理/u87.png b/web/main/static/images/通道管理/u87.png similarity index 100% rename from web/static/images/通道管理/u87.png rename to web/main/static/images/通道管理/u87.png diff --git a/web/static/plugins/debug/debug.js b/web/main/static/plugins/debug/debug.js similarity index 100% rename from web/static/plugins/debug/debug.js rename to web/main/static/plugins/debug/debug.js diff --git a/web/static/plugins/debug/styles/debug.css b/web/main/static/plugins/debug/styles/debug.css similarity index 100% rename from web/static/plugins/debug/styles/debug.css rename to web/main/static/plugins/debug/styles/debug.css diff --git a/web/static/plugins/debug/styles/images/console_panel_off.svg b/web/main/static/plugins/debug/styles/images/console_panel_off.svg similarity index 100% rename from web/static/plugins/debug/styles/images/console_panel_off.svg rename to web/main/static/plugins/debug/styles/images/console_panel_off.svg diff --git a/web/static/plugins/debug/styles/images/console_panel_on.svg b/web/main/static/plugins/debug/styles/images/console_panel_on.svg similarity index 100% rename from web/static/plugins/debug/styles/images/console_panel_on.svg rename to web/main/static/plugins/debug/styles/images/console_panel_on.svg diff --git a/web/static/plugins/page_notes/page_notes.js b/web/main/static/plugins/page_notes/page_notes.js similarity index 100% rename from web/static/plugins/page_notes/page_notes.js rename to web/main/static/plugins/page_notes/page_notes.js diff --git a/web/static/plugins/page_notes/styles/images/notes_panel_off.svg b/web/main/static/plugins/page_notes/styles/images/notes_panel_off.svg similarity index 100% rename from web/static/plugins/page_notes/styles/images/notes_panel_off.svg rename to web/main/static/plugins/page_notes/styles/images/notes_panel_off.svg diff --git a/web/static/plugins/page_notes/styles/images/notes_panel_on.svg b/web/main/static/plugins/page_notes/styles/images/notes_panel_on.svg similarity index 100% rename from web/static/plugins/page_notes/styles/images/notes_panel_on.svg rename to web/main/static/plugins/page_notes/styles/images/notes_panel_on.svg diff --git a/web/static/plugins/page_notes/styles/page_notes.css b/web/main/static/plugins/page_notes/styles/page_notes.css similarity index 100% rename from web/static/plugins/page_notes/styles/page_notes.css rename to web/main/static/plugins/page_notes/styles/page_notes.css diff --git a/web/static/plugins/recordplay/recordplay.js b/web/main/static/plugins/recordplay/recordplay.js similarity index 100% rename from web/static/plugins/recordplay/recordplay.js rename to web/main/static/plugins/recordplay/recordplay.js diff --git a/web/static/plugins/recordplay/styles/recordplay.css b/web/main/static/plugins/recordplay/styles/recordplay.css similarity index 100% rename from web/static/plugins/recordplay/styles/recordplay.css rename to web/main/static/plugins/recordplay/styles/recordplay.css diff --git a/web/static/plugins/sitemap/sitemap.js b/web/main/static/plugins/sitemap/sitemap.js similarity index 100% rename from web/static/plugins/sitemap/sitemap.js rename to web/main/static/plugins/sitemap/sitemap.js diff --git a/web/static/plugins/sitemap/styles/images/back_keys.svg b/web/main/static/plugins/sitemap/styles/images/back_keys.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/back_keys.svg rename to web/main/static/plugins/sitemap/styles/images/back_keys.svg diff --git a/web/static/plugins/sitemap/styles/images/closed_item.svg b/web/main/static/plugins/sitemap/styles/images/closed_item.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/closed_item.svg rename to web/main/static/plugins/sitemap/styles/images/closed_item.svg diff --git a/web/static/plugins/sitemap/styles/images/flow.svg b/web/main/static/plugins/sitemap/styles/images/flow.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/flow.svg rename to web/main/static/plugins/sitemap/styles/images/flow.svg diff --git a/web/static/plugins/sitemap/styles/images/folder_closed_blue.svg b/web/main/static/plugins/sitemap/styles/images/folder_closed_blue.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/folder_closed_blue.svg rename to web/main/static/plugins/sitemap/styles/images/folder_closed_blue.svg diff --git a/web/static/plugins/sitemap/styles/images/forward_keys.svg b/web/main/static/plugins/sitemap/styles/images/forward_keys.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/forward_keys.svg rename to web/main/static/plugins/sitemap/styles/images/forward_keys.svg diff --git a/web/static/plugins/sitemap/styles/images/left_arrow.svg b/web/main/static/plugins/sitemap/styles/images/left_arrow.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/left_arrow.svg rename to web/main/static/plugins/sitemap/styles/images/left_arrow.svg diff --git a/web/static/plugins/sitemap/styles/images/open_item.svg b/web/main/static/plugins/sitemap/styles/images/open_item.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/open_item.svg rename to web/main/static/plugins/sitemap/styles/images/open_item.svg diff --git a/web/static/plugins/sitemap/styles/images/page_lt_grey.svg b/web/main/static/plugins/sitemap/styles/images/page_lt_grey.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/page_lt_grey.svg rename to web/main/static/plugins/sitemap/styles/images/page_lt_grey.svg diff --git a/web/static/plugins/sitemap/styles/images/right_arrow.svg b/web/main/static/plugins/sitemap/styles/images/right_arrow.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/right_arrow.svg rename to web/main/static/plugins/sitemap/styles/images/right_arrow.svg diff --git a/web/static/plugins/sitemap/styles/images/search_off.svg b/web/main/static/plugins/sitemap/styles/images/search_off.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/search_off.svg rename to web/main/static/plugins/sitemap/styles/images/search_off.svg diff --git a/web/static/plugins/sitemap/styles/images/search_on.svg b/web/main/static/plugins/sitemap/styles/images/search_on.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/search_on.svg rename to web/main/static/plugins/sitemap/styles/images/search_on.svg diff --git a/web/static/plugins/sitemap/styles/images/sitemap_panel_off.svg b/web/main/static/plugins/sitemap/styles/images/sitemap_panel_off.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/sitemap_panel_off.svg rename to web/main/static/plugins/sitemap/styles/images/sitemap_panel_off.svg diff --git a/web/static/plugins/sitemap/styles/images/sitemap_panel_on.svg b/web/main/static/plugins/sitemap/styles/images/sitemap_panel_on.svg similarity index 100% rename from web/static/plugins/sitemap/styles/images/sitemap_panel_on.svg rename to web/main/static/plugins/sitemap/styles/images/sitemap_panel_on.svg diff --git a/web/static/plugins/sitemap/styles/sitemap.css b/web/main/static/plugins/sitemap/styles/sitemap.css similarity index 100% rename from web/static/plugins/sitemap/styles/sitemap.css rename to web/main/static/plugins/sitemap/styles/sitemap.css diff --git a/web/static/resources/Other.html b/web/main/static/resources/Other.html similarity index 100% rename from web/static/resources/Other.html rename to web/main/static/resources/Other.html diff --git a/web/static/resources/chrome/allow-access.png b/web/main/static/resources/chrome/allow-access.png similarity index 100% rename from web/static/resources/chrome/allow-access.png rename to web/main/static/resources/chrome/allow-access.png diff --git a/web/static/resources/chrome/axure-chrome-extension.crx b/web/main/static/resources/chrome/axure-chrome-extension.crx similarity index 100% rename from web/static/resources/chrome/axure-chrome-extension.crx rename to web/main/static/resources/chrome/axure-chrome-extension.crx diff --git a/web/static/resources/chrome/axure_logo.png b/web/main/static/resources/chrome/axure_logo.png similarity index 100% rename from web/static/resources/chrome/axure_logo.png rename to web/main/static/resources/chrome/axure_logo.png diff --git a/web/static/resources/chrome/chrome.html b/web/main/static/resources/chrome/chrome.html similarity index 100% rename from web/static/resources/chrome/chrome.html rename to web/main/static/resources/chrome/chrome.html diff --git a/web/static/resources/chrome/details.png b/web/main/static/resources/chrome/details.png similarity index 100% rename from web/static/resources/chrome/details.png rename to web/main/static/resources/chrome/details.png diff --git a/web/static/resources/chrome/extensions.png b/web/main/static/resources/chrome/extensions.png similarity index 100% rename from web/static/resources/chrome/extensions.png rename to web/main/static/resources/chrome/extensions.png diff --git a/web/static/resources/chrome/firefox.html b/web/main/static/resources/chrome/firefox.html similarity index 100% rename from web/static/resources/chrome/firefox.html rename to web/main/static/resources/chrome/firefox.html diff --git a/web/static/resources/chrome/preview-rp.png b/web/main/static/resources/chrome/preview-rp.png similarity index 100% rename from web/static/resources/chrome/preview-rp.png rename to web/main/static/resources/chrome/preview-rp.png diff --git a/web/static/resources/chrome/safari.html b/web/main/static/resources/chrome/safari.html similarity index 100% rename from web/static/resources/chrome/safari.html rename to web/main/static/resources/chrome/safari.html diff --git a/web/static/resources/chrome/safari_advanced.png b/web/main/static/resources/chrome/safari_advanced.png similarity index 100% rename from web/static/resources/chrome/safari_advanced.png rename to web/main/static/resources/chrome/safari_advanced.png diff --git a/web/static/resources/chrome/safari_restrictions.png b/web/main/static/resources/chrome/safari_restrictions.png similarity index 100% rename from web/static/resources/chrome/safari_restrictions.png rename to web/main/static/resources/chrome/safari_restrictions.png diff --git a/web/static/resources/chrome/splitter.gif b/web/main/static/resources/chrome/splitter.gif similarity index 100% rename from web/static/resources/chrome/splitter.gif rename to web/main/static/resources/chrome/splitter.gif diff --git a/web/static/resources/chrome/splitter.png b/web/main/static/resources/chrome/splitter.png similarity index 100% rename from web/static/resources/chrome/splitter.png rename to web/main/static/resources/chrome/splitter.png diff --git a/web/static/resources/css/axure_rp_page.css b/web/main/static/resources/css/axure_rp_page.css similarity index 100% rename from web/static/resources/css/axure_rp_page.css rename to web/main/static/resources/css/axure_rp_page.css diff --git a/web/static/resources/css/default.css b/web/main/static/resources/css/default.css similarity index 100% rename from web/static/resources/css/default.css rename to web/main/static/resources/css/default.css diff --git a/web/static/resources/css/images/images.html b/web/main/static/resources/css/images/images.html similarity index 100% rename from web/static/resources/css/images/images.html rename to web/main/static/resources/css/images/images.html diff --git a/web/static/resources/css/images/newwindow.gif b/web/main/static/resources/css/images/newwindow.gif similarity index 100% rename from web/static/resources/css/images/newwindow.gif rename to web/main/static/resources/css/images/newwindow.gif diff --git a/web/static/resources/css/images/note.gif b/web/main/static/resources/css/images/note.gif similarity index 100% rename from web/static/resources/css/images/note.gif rename to web/main/static/resources/css/images/note.gif diff --git a/web/static/resources/css/images/touch.cur b/web/main/static/resources/css/images/touch.cur similarity index 100% rename from web/static/resources/css/images/touch.cur rename to web/main/static/resources/css/images/touch.cur diff --git a/web/static/resources/css/images/touch.svg b/web/main/static/resources/css/images/touch.svg similarity index 100% rename from web/static/resources/css/images/touch.svg rename to web/main/static/resources/css/images/touch.svg diff --git a/web/static/resources/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/web/main/static/resources/css/images/ui-bg_flat_0_aaaaaa_40x100.png similarity index 100% rename from web/static/resources/css/images/ui-bg_flat_0_aaaaaa_40x100.png rename to web/main/static/resources/css/images/ui-bg_flat_0_aaaaaa_40x100.png diff --git a/web/static/resources/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/web/main/static/resources/css/images/ui-bg_glass_55_fbf9ee_1x400.png similarity index 100% rename from web/static/resources/css/images/ui-bg_glass_55_fbf9ee_1x400.png rename to web/main/static/resources/css/images/ui-bg_glass_55_fbf9ee_1x400.png diff --git a/web/static/resources/css/images/ui-bg_glass_65_ffffff_1x400.png b/web/main/static/resources/css/images/ui-bg_glass_65_ffffff_1x400.png similarity index 100% rename from web/static/resources/css/images/ui-bg_glass_65_ffffff_1x400.png rename to web/main/static/resources/css/images/ui-bg_glass_65_ffffff_1x400.png diff --git a/web/static/resources/css/images/ui-bg_glass_75_dadada_1x400.png b/web/main/static/resources/css/images/ui-bg_glass_75_dadada_1x400.png similarity index 100% rename from web/static/resources/css/images/ui-bg_glass_75_dadada_1x400.png rename to web/main/static/resources/css/images/ui-bg_glass_75_dadada_1x400.png diff --git a/web/static/resources/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/web/main/static/resources/css/images/ui-bg_glass_75_e6e6e6_1x400.png similarity index 100% rename from web/static/resources/css/images/ui-bg_glass_75_e6e6e6_1x400.png rename to web/main/static/resources/css/images/ui-bg_glass_75_e6e6e6_1x400.png diff --git a/web/static/resources/css/images/ui-bg_glass_75_ffffff_1x400.png b/web/main/static/resources/css/images/ui-bg_glass_75_ffffff_1x400.png similarity index 100% rename from web/static/resources/css/images/ui-bg_glass_75_ffffff_1x400.png rename to web/main/static/resources/css/images/ui-bg_glass_75_ffffff_1x400.png diff --git a/web/static/resources/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/web/main/static/resources/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png similarity index 100% rename from web/static/resources/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png rename to web/main/static/resources/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png diff --git a/web/static/resources/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png b/web/main/static/resources/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png similarity index 100% rename from web/static/resources/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png rename to web/main/static/resources/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png diff --git a/web/static/resources/css/images/ui-icons_222222_256x240.png b/web/main/static/resources/css/images/ui-icons_222222_256x240.png similarity index 100% rename from web/static/resources/css/images/ui-icons_222222_256x240.png rename to web/main/static/resources/css/images/ui-icons_222222_256x240.png diff --git a/web/static/resources/css/images/ui-icons_2e83ff_256x240.png b/web/main/static/resources/css/images/ui-icons_2e83ff_256x240.png similarity index 100% rename from web/static/resources/css/images/ui-icons_2e83ff_256x240.png rename to web/main/static/resources/css/images/ui-icons_2e83ff_256x240.png diff --git a/web/static/resources/css/images/ui-icons_454545_256x240.png b/web/main/static/resources/css/images/ui-icons_454545_256x240.png similarity index 100% rename from web/static/resources/css/images/ui-icons_454545_256x240.png rename to web/main/static/resources/css/images/ui-icons_454545_256x240.png diff --git a/web/static/resources/css/images/ui-icons_888888_256x240.png b/web/main/static/resources/css/images/ui-icons_888888_256x240.png similarity index 100% rename from web/static/resources/css/images/ui-icons_888888_256x240.png rename to web/main/static/resources/css/images/ui-icons_888888_256x240.png diff --git a/web/static/resources/css/images/ui-icons_cd0a0a_256x240.png b/web/main/static/resources/css/images/ui-icons_cd0a0a_256x240.png similarity index 100% rename from web/static/resources/css/images/ui-icons_cd0a0a_256x240.png rename to web/main/static/resources/css/images/ui-icons_cd0a0a_256x240.png diff --git a/web/static/resources/css/jquery-ui-themes.css b/web/main/static/resources/css/jquery-ui-themes.css similarity index 100% rename from web/static/resources/css/jquery-ui-themes.css rename to web/main/static/resources/css/jquery-ui-themes.css diff --git a/web/static/resources/css/previewfonts.css b/web/main/static/resources/css/previewfonts.css similarity index 100% rename from web/static/resources/css/previewfonts.css rename to web/main/static/resources/css/previewfonts.css diff --git a/web/static/resources/css/previewfonts/SourceSansPro-OpenFontLicense.txt b/web/main/static/resources/css/previewfonts/SourceSansPro-OpenFontLicense.txt similarity index 100% rename from web/static/resources/css/previewfonts/SourceSansPro-OpenFontLicense.txt rename to web/main/static/resources/css/previewfonts/SourceSansPro-OpenFontLicense.txt diff --git a/web/static/resources/css/previewfonts/SourceSansPro-Regular.woff2 b/web/main/static/resources/css/previewfonts/SourceSansPro-Regular.woff2 similarity index 100% rename from web/static/resources/css/previewfonts/SourceSansPro-Regular.woff2 rename to web/main/static/resources/css/previewfonts/SourceSansPro-Regular.woff2 diff --git a/web/static/resources/css/previewfonts/SourceSansPro-Semibold.woff2 b/web/main/static/resources/css/previewfonts/SourceSansPro-Semibold.woff2 similarity index 100% rename from web/static/resources/css/previewfonts/SourceSansPro-Semibold.woff2 rename to web/main/static/resources/css/previewfonts/SourceSansPro-Semibold.woff2 diff --git a/web/static/resources/css/reset.css b/web/main/static/resources/css/reset.css similarity index 100% rename from web/static/resources/css/reset.css rename to web/main/static/resources/css/reset.css diff --git a/web/static/resources/expand.html b/web/main/static/resources/expand.html similarity index 100% rename from web/static/resources/expand.html rename to web/main/static/resources/expand.html diff --git a/web/static/resources/images/axure9_logo.svg b/web/main/static/resources/images/axure9_logo.svg similarity index 100% rename from web/static/resources/images/axure9_logo.svg rename to web/main/static/resources/images/axure9_logo.svg diff --git a/web/static/resources/images/axure_logo.png b/web/main/static/resources/images/axure_logo.png similarity index 100% rename from web/static/resources/images/axure_logo.png rename to web/main/static/resources/images/axure_logo.png diff --git a/web/static/resources/images/axure_logo.svg b/web/main/static/resources/images/axure_logo.svg similarity index 100% rename from web/static/resources/images/axure_logo.svg rename to web/main/static/resources/images/axure_logo.svg diff --git a/web/static/resources/images/caret_down.svg b/web/main/static/resources/images/caret_down.svg similarity index 100% rename from web/static/resources/images/caret_down.svg rename to web/main/static/resources/images/caret_down.svg diff --git a/web/static/resources/images/caret_down_off.svg b/web/main/static/resources/images/caret_down_off.svg similarity index 100% rename from web/static/resources/images/caret_down_off.svg rename to web/main/static/resources/images/caret_down_off.svg diff --git a/web/static/resources/images/close_x.svg b/web/main/static/resources/images/close_x.svg similarity index 100% rename from web/static/resources/images/close_x.svg rename to web/main/static/resources/images/close_x.svg diff --git a/web/static/resources/images/close_x_minimize.svg b/web/main/static/resources/images/close_x_minimize.svg similarity index 100% rename from web/static/resources/images/close_x_minimize.svg rename to web/main/static/resources/images/close_x_minimize.svg diff --git a/web/static/resources/images/divider.png b/web/main/static/resources/images/divider.png similarity index 100% rename from web/static/resources/images/divider.png rename to web/main/static/resources/images/divider.png diff --git a/web/static/resources/images/divider.svg b/web/main/static/resources/images/divider.svg similarity index 100% rename from web/static/resources/images/divider.svg rename to web/main/static/resources/images/divider.svg diff --git a/web/static/resources/images/exit.svg b/web/main/static/resources/images/exit.svg similarity index 100% rename from web/static/resources/images/exit.svg rename to web/main/static/resources/images/exit.svg diff --git a/web/static/resources/images/favicon_play.ico b/web/main/static/resources/images/favicon_play.ico similarity index 100% rename from web/static/resources/images/favicon_play.ico rename to web/main/static/resources/images/favicon_play.ico diff --git a/web/static/resources/images/overflow-icon.svg b/web/main/static/resources/images/overflow-icon.svg similarity index 100% rename from web/static/resources/images/overflow-icon.svg rename to web/main/static/resources/images/overflow-icon.svg diff --git a/web/static/resources/images/overflow_checkmark.svg b/web/main/static/resources/images/overflow_checkmark.svg similarity index 100% rename from web/static/resources/images/overflow_checkmark.svg rename to web/main/static/resources/images/overflow_checkmark.svg diff --git a/web/static/resources/images/overflow_icon_off.svg b/web/main/static/resources/images/overflow_icon_off.svg similarity index 100% rename from web/static/resources/images/overflow_icon_off.svg rename to web/main/static/resources/images/overflow_icon_off.svg diff --git a/web/static/resources/images/refresh.svg b/web/main/static/resources/images/refresh.svg similarity index 100% rename from web/static/resources/images/refresh.svg rename to web/main/static/resources/images/refresh.svg diff --git a/web/static/resources/images/resize.svg b/web/main/static/resources/images/resize.svg similarity index 100% rename from web/static/resources/images/resize.svg rename to web/main/static/resources/images/resize.svg diff --git a/web/static/resources/images/return.svg b/web/main/static/resources/images/return.svg similarity index 100% rename from web/static/resources/images/return.svg rename to web/main/static/resources/images/return.svg diff --git a/web/static/resources/images/transparent.gif b/web/main/static/resources/images/transparent.gif similarity index 100% rename from web/static/resources/images/transparent.gif rename to web/main/static/resources/images/transparent.gif diff --git a/web/static/resources/images/views-icon.svg b/web/main/static/resources/images/views-icon.svg similarity index 100% rename from web/static/resources/images/views-icon.svg rename to web/main/static/resources/images/views-icon.svg diff --git a/web/static/resources/reload.html b/web/main/static/resources/reload.html similarity index 100% rename from web/static/resources/reload.html rename to web/main/static/resources/reload.html diff --git a/web/static/resources/scripts/aiortc-client-new.js b/web/main/static/resources/scripts/aiortc-client-new.js similarity index 96% rename from web/static/resources/scripts/aiortc-client-new.js rename to web/main/static/resources/scripts/aiortc-client-new.js index 009f13b..e1f5088 100644 --- a/web/static/resources/scripts/aiortc-client-new.js +++ b/web/main/static/resources/scripts/aiortc-client-new.js @@ -20,7 +20,7 @@ function negotiate() { }); }).then(() => { var offer = pc.localDescription; - return fetch('/offer', { + return fetch('/api/offer', { body: JSON.stringify({ sdp: offer.sdp, type: offer.type, @@ -63,3 +63,7 @@ function showView(){ }); } } + +function getChannellist(){ + +} diff --git a/web/static/resources/scripts/axure/action.js b/web/main/static/resources/scripts/axure/action.js similarity index 100% rename from web/static/resources/scripts/axure/action.js rename to web/main/static/resources/scripts/axure/action.js diff --git a/web/static/resources/scripts/axure/adaptive.js b/web/main/static/resources/scripts/axure/adaptive.js similarity index 100% rename from web/static/resources/scripts/axure/adaptive.js rename to web/main/static/resources/scripts/axure/adaptive.js diff --git a/web/static/resources/scripts/axure/annotation.js b/web/main/static/resources/scripts/axure/annotation.js similarity index 100% rename from web/static/resources/scripts/axure/annotation.js rename to web/main/static/resources/scripts/axure/annotation.js diff --git a/web/static/resources/scripts/axure/axQuery.js b/web/main/static/resources/scripts/axure/axQuery.js similarity index 100% rename from web/static/resources/scripts/axure/axQuery.js rename to web/main/static/resources/scripts/axure/axQuery.js diff --git a/web/static/resources/scripts/axure/axQuery.std.js b/web/main/static/resources/scripts/axure/axQuery.std.js similarity index 100% rename from web/static/resources/scripts/axure/axQuery.std.js rename to web/main/static/resources/scripts/axure/axQuery.std.js diff --git a/web/static/resources/scripts/axure/doc.js b/web/main/static/resources/scripts/axure/doc.js similarity index 100% rename from web/static/resources/scripts/axure/doc.js rename to web/main/static/resources/scripts/axure/doc.js diff --git a/web/static/resources/scripts/axure/drag.js b/web/main/static/resources/scripts/axure/drag.js similarity index 100% rename from web/static/resources/scripts/axure/drag.js rename to web/main/static/resources/scripts/axure/drag.js diff --git a/web/static/resources/scripts/axure/events.js b/web/main/static/resources/scripts/axure/events.js similarity index 100% rename from web/static/resources/scripts/axure/events.js rename to web/main/static/resources/scripts/axure/events.js diff --git a/web/static/resources/scripts/axure/expr.js b/web/main/static/resources/scripts/axure/expr.js similarity index 100% rename from web/static/resources/scripts/axure/expr.js rename to web/main/static/resources/scripts/axure/expr.js diff --git a/web/static/resources/scripts/axure/flyout.js b/web/main/static/resources/scripts/axure/flyout.js similarity index 100% rename from web/static/resources/scripts/axure/flyout.js rename to web/main/static/resources/scripts/axure/flyout.js diff --git a/web/static/resources/scripts/axure/geometry.js b/web/main/static/resources/scripts/axure/geometry.js similarity index 100% rename from web/static/resources/scripts/axure/geometry.js rename to web/main/static/resources/scripts/axure/geometry.js diff --git a/web/static/resources/scripts/axure/globals.js b/web/main/static/resources/scripts/axure/globals.js similarity index 100% rename from web/static/resources/scripts/axure/globals.js rename to web/main/static/resources/scripts/axure/globals.js diff --git a/web/static/resources/scripts/axure/ie.js b/web/main/static/resources/scripts/axure/ie.js similarity index 100% rename from web/static/resources/scripts/axure/ie.js rename to web/main/static/resources/scripts/axure/ie.js diff --git a/web/static/resources/scripts/axure/init.temp.js b/web/main/static/resources/scripts/axure/init.temp.js similarity index 100% rename from web/static/resources/scripts/axure/init.temp.js rename to web/main/static/resources/scripts/axure/init.temp.js diff --git a/web/static/resources/scripts/axure/ios.js b/web/main/static/resources/scripts/axure/ios.js similarity index 100% rename from web/static/resources/scripts/axure/ios.js rename to web/main/static/resources/scripts/axure/ios.js diff --git a/web/static/resources/scripts/axure/jquery.nicescroll.min.js b/web/main/static/resources/scripts/axure/jquery.nicescroll.min.js similarity index 100% rename from web/static/resources/scripts/axure/jquery.nicescroll.min.js rename to web/main/static/resources/scripts/axure/jquery.nicescroll.min.js diff --git a/web/static/resources/scripts/axure/legacy.js b/web/main/static/resources/scripts/axure/legacy.js similarity index 100% rename from web/static/resources/scripts/axure/legacy.js rename to web/main/static/resources/scripts/axure/legacy.js diff --git a/web/static/resources/scripts/axure/math.js b/web/main/static/resources/scripts/axure/math.js similarity index 100% rename from web/static/resources/scripts/axure/math.js rename to web/main/static/resources/scripts/axure/math.js diff --git a/web/static/resources/scripts/axure/model.js b/web/main/static/resources/scripts/axure/model.js similarity index 100% rename from web/static/resources/scripts/axure/model.js rename to web/main/static/resources/scripts/axure/model.js diff --git a/web/static/resources/scripts/axure/move.js b/web/main/static/resources/scripts/axure/move.js similarity index 100% rename from web/static/resources/scripts/axure/move.js rename to web/main/static/resources/scripts/axure/move.js diff --git a/web/static/resources/scripts/axure/recording.js b/web/main/static/resources/scripts/axure/recording.js similarity index 100% rename from web/static/resources/scripts/axure/recording.js rename to web/main/static/resources/scripts/axure/recording.js diff --git a/web/static/resources/scripts/axure/repeater.js b/web/main/static/resources/scripts/axure/repeater.js similarity index 100% rename from web/static/resources/scripts/axure/repeater.js rename to web/main/static/resources/scripts/axure/repeater.js diff --git a/web/static/resources/scripts/axure/sto.js b/web/main/static/resources/scripts/axure/sto.js similarity index 100% rename from web/static/resources/scripts/axure/sto.js rename to web/main/static/resources/scripts/axure/sto.js diff --git a/web/static/resources/scripts/axure/style.js b/web/main/static/resources/scripts/axure/style.js similarity index 100% rename from web/static/resources/scripts/axure/style.js rename to web/main/static/resources/scripts/axure/style.js diff --git a/web/static/resources/scripts/axure/tree.js b/web/main/static/resources/scripts/axure/tree.js similarity index 100% rename from web/static/resources/scripts/axure/tree.js rename to web/main/static/resources/scripts/axure/tree.js diff --git a/web/static/resources/scripts/axure/utils.temp.js b/web/main/static/resources/scripts/axure/utils.temp.js similarity index 100% rename from web/static/resources/scripts/axure/utils.temp.js rename to web/main/static/resources/scripts/axure/utils.temp.js diff --git a/web/static/resources/scripts/axure/variables.js b/web/main/static/resources/scripts/axure/variables.js similarity index 100% rename from web/static/resources/scripts/axure/variables.js rename to web/main/static/resources/scripts/axure/variables.js diff --git a/web/static/resources/scripts/axure/viewer.js b/web/main/static/resources/scripts/axure/viewer.js similarity index 100% rename from web/static/resources/scripts/axure/viewer.js rename to web/main/static/resources/scripts/axure/viewer.js diff --git a/web/static/resources/scripts/axure/visibility.js b/web/main/static/resources/scripts/axure/visibility.js similarity index 100% rename from web/static/resources/scripts/axure/visibility.js rename to web/main/static/resources/scripts/axure/visibility.js diff --git a/web/static/resources/scripts/axutils.js b/web/main/static/resources/scripts/axutils.js similarity index 100% rename from web/static/resources/scripts/axutils.js rename to web/main/static/resources/scripts/axutils.js diff --git a/web/static/resources/scripts/client.js b/web/main/static/resources/scripts/client.js similarity index 100% rename from web/static/resources/scripts/client.js rename to web/main/static/resources/scripts/client.js diff --git a/web/static/resources/scripts/jquery-1.7.1.min.js b/web/main/static/resources/scripts/jquery-1.7.1.min.js similarity index 100% rename from web/static/resources/scripts/jquery-1.7.1.min.js rename to web/main/static/resources/scripts/jquery-1.7.1.min.js diff --git a/web/static/resources/scripts/jquery-3.2.1.min.js b/web/main/static/resources/scripts/jquery-3.2.1.min.js similarity index 100% rename from web/static/resources/scripts/jquery-3.2.1.min.js rename to web/main/static/resources/scripts/jquery-3.2.1.min.js diff --git a/web/static/resources/scripts/jquery-ui-1.8.10.custom.min.js b/web/main/static/resources/scripts/jquery-ui-1.8.10.custom.min.js similarity index 100% rename from web/static/resources/scripts/jquery-ui-1.8.10.custom.min.js rename to web/main/static/resources/scripts/jquery-ui-1.8.10.custom.min.js diff --git a/web/static/resources/scripts/messagecenter.js b/web/main/static/resources/scripts/messagecenter.js similarity index 100% rename from web/static/resources/scripts/messagecenter.js rename to web/main/static/resources/scripts/messagecenter.js diff --git a/web/static/resources/scripts/player/axplayer.js b/web/main/static/resources/scripts/player/axplayer.js similarity index 100% rename from web/static/resources/scripts/player/axplayer.js rename to web/main/static/resources/scripts/player/axplayer.js diff --git a/web/static/resources/scripts/player/init.js b/web/main/static/resources/scripts/player/init.js similarity index 100% rename from web/static/resources/scripts/player/init.js rename to web/main/static/resources/scripts/player/init.js diff --git a/web/static/resources/scripts/player/splitter.js b/web/main/static/resources/scripts/player/splitter.js similarity index 100% rename from web/static/resources/scripts/player/splitter.js rename to web/main/static/resources/scripts/player/splitter.js diff --git a/web/static/resources/scripts/socket.io.min.js b/web/main/static/resources/scripts/socket.io.min.js similarity index 100% rename from web/static/resources/scripts/socket.io.min.js rename to web/main/static/resources/scripts/socket.io.min.js diff --git a/web/main/templates/index.html b/web/main/templates/index.html new file mode 100644 index 0000000..9015b2c --- /dev/null +++ b/web/main/templates/index.html @@ -0,0 +1,12 @@ + + + + + + YOLOv5 Real-Time Detection + + +

YOLOv5 Real-Time Detection

+ + + diff --git a/web/main/templates/index_webrtc.html b/web/main/templates/index_webrtc.html new file mode 100644 index 0000000..671d43f --- /dev/null +++ b/web/main/templates/index_webrtc.html @@ -0,0 +1,45 @@ + + + + WebRTC Video Stream + + +

WebRTC Video Stream

+ + + + diff --git a/web/main/templates/upload.html b/web/main/templates/upload.html new file mode 100644 index 0000000..c6099cf --- /dev/null +++ b/web/main/templates/upload.html @@ -0,0 +1,23 @@ + + + + + Upload File + + +

Upload new File

+
+ + +
+ {% with messages = get_flashed_messages() %} + {% if messages %} +
    + {% for message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + + diff --git a/web/templates/实时预览.html b/web/main/templates/实时预览.html similarity index 89% rename from web/templates/实时预览.html rename to web/main/templates/实时预览.html index 91db6ad..cb5696c 100644 --- a/web/templates/实时预览.html +++ b/web/main/templates/实时预览.html @@ -172,25 +172,44 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if error %} +
{{ error }}
+ {% endif %}
diff --git a/web/templates/算法管理.html b/web/main/templates/算法管理.html similarity index 100% rename from web/templates/算法管理.html rename to web/main/templates/算法管理.html diff --git a/web/templates/系统管理.html b/web/main/templates/系统管理.html similarity index 100% rename from web/templates/系统管理.html rename to web/main/templates/系统管理.html diff --git a/web/templates/通道管理.html b/web/main/templates/通道管理.html similarity index 100% rename from web/templates/通道管理.html rename to web/main/templates/通道管理.html diff --git a/web/suggestions.db b/web/suggestions.db deleted file mode 100644 index 9231b0c..0000000 Binary files a/web/suggestions.db and /dev/null differ diff --git a/web/templates/index.html b/web/templates/index.html deleted file mode 100644 index a4a97a9..0000000 --- a/web/templates/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - Submit Suggestion - - -

Submit Your Suggestion

-
-
-
- -
- - diff --git a/web/templates/index_new.html b/web/templates/index_new.html deleted file mode 100644 index 3605bc9..0000000 --- a/web/templates/index_new.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - WebRTC webcam - - - - -
- - -
- - - -
-

Media

- - - -
- - - - diff --git a/zfbox.db b/zfbox.db new file mode 100644 index 0000000..d7ffc16 Binary files /dev/null and b/zfbox.db differ