diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/billiard.iml b/.idea/billiard.iml new file mode 100644 index 0000000..f721fe6 --- /dev/null +++ b/.idea/billiard.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..5d56808 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,461 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a95a050 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..42b05f7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app.py b/app.py index abdc605..665d848 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,13 @@ import cv2 import json import time +import torch import requests +import threading +import numpy as np import supervision as sv +from datetime import datetime from ultralytics import YOLO from flask import Flask,request,Response from multiprocessing import Process, Event @@ -39,33 +43,78 @@ def stop(id): @app.route('/show',methods=['POST']) def show(): - print(request.data) + # print(request.json) + data = request.json["content"] + info(data) return 'ok' +def info(data): + balls = data["balls"] + table = data["table"] + text = f"table:{table:>3} " + for i in range(16): + ball = "#" if "ball"+str(i) in balls else " " + text += f"| {i} : {ball} " + print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),text) + def worker(id,source,region=None,stream=False): if region == None: region = 10 else: region = int(region) + print("start loading model...",id,source,region) model = YOLO('./best.pt') + print("start loaded model!!!") balls = {} count = 0 - print(source) + delay = 0 + bgn = 0 try: cap = cv2.VideoCapture(source) if not cap.isOpened(): print("Error opening video stream.") + if source.endswith(".mp4"): + fps = cap.get(cv2.CAP_PROP_FPS) + delay = 1000.0 / fps + print("start video stream... ", fps, delay) while True: + use = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + if bgn != 0 and delay != 0 and time.time()*1000 - bgn < 2*delay: + print("sleep",2*delay - (time.time()*1000 - bgn)) + time.sleep((2*delay - (time.time()*1000 - bgn))/1000) + bgn = (time.time()*1000) ret, frame = cap.read() + del(ret) + del(frame) + ret, frame = cap.read() + use += " read:"+str((time.time()*1000) - bgn) if not ret: print("Error read video stream.") - break - result = model.track(frame,show=False,stream=False,persist=True,device=int(id)%4) + cap = cv2.VideoCapture(source) + if not cap.isOpened(): + print("Error opening video stream..") + cap = cv2.VideoCapture(source) + time.sleep(1) + if not cap.isOpened(): + print("Error opening video stream...") + time.sleep(3) + if not cap.isOpened(): + print("Error opening video stream....") + break + continue + result = model.track(frame,device=int(id)%4,tracker='botsort.yaml') + use += " track:"+str((time.time()*1000) - bgn) + del(ret) + del(frame) result = result[0] detections = sv.Detections.from_yolov8(result) if result.boxes.id is not None: detections.tracker_id = result.boxes.id.cpu().numpy().astype(int) - detections = detections[(detections.tracker_id != None)] + else: + detections.tracker_id = np.array([]) + detections.conf = np.array([]) + detections.xyxy=np.empty((0, 4), dtype=np.float32) + # detections = detections[(detections.tracker_id != None)] count += 1 names = {} @@ -84,15 +133,21 @@ def worker(id,source,region=None,stream=False): balls[name] = ball names[name] = True names = {} + use += " names:"+str((time.time()*1000) - bgn) if count < region: continue - json_data = json.dumps({"table":id,"balls":balls,"time":int(time.time()*1000)}) + data = {"table":id,"balls":balls,"time":int(time.time()*1000)} + info(data) + json_data = json.dumps(data) balls = {} count = 0 + use += " dump:"+str((time.time()*1000) - bgn) if not stream: yield json_data else: yield f"data: {json_data}\n\n" + use += " yield:"+str((time.time()*1000) - bgn) + print("model.track",use) except GeneratorExit: print("Client disconnected at", time.ctime()) @@ -102,21 +157,47 @@ def workerloop(stop_event,id,source,target=None,region=None): try: gen = worker(id,source,region) for data in gen: + use = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + bgn = (time.time()*1000) if stop_event.is_set(): break json_data = json.loads(data) - # 设置请求头 - headers = { - "Content-Type": "application/json" - } + use += " loads:"+str((time.time()*1000) - bgn) text = json.dumps({"content":json_data}) - print(text) - response = requests.post(target, data=text, headers=headers) + use += " dumps:"+str((time.time()*1000) - bgn) - print(response.text) + # 创建并启动线程 + thread = threading.Thread(target=post_request, args=(target, text)) + thread.start() + + use += " post:"+str((time.time()*1000) - bgn) + print("workerloop",use) finally: gen.close() +def post_request(url, data): + use = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + bgn = (time.time()*1000) + headers = { + "Content-Type": "application/json" + } + response = requests.post(url, data=data,headers=headers) + use += " post:"+str((time.time()*1000) - bgn ) +"ms" + print("post_request",use,response.text) +# import pstats +# import cProfile +# def test_function(): +# url = "rtmp://rtmp03open.ys7.com:1935/v3/openlive/L05874022_1_1?expire=1726389273&id=625006970167889920&t=2c7f371063e4ad672cb7a7c34b2a236e9ff37b02052f818c9c4fe7ae9632cd0e&ev=100" +# gen = worker(0,url,5) +# count = 0 +# for data in gen: +# count += 1 +# if count > 10: +# break + if __name__ == '__main__': + # cProfile.run('test_function()', 'test_function.profile') + # p = pstats.Stats('test_function.profile') + # p.sort_stats('cumulative').print_stats(100) # Top 10 by cumulative time app.run("0.0.0.0",threaded=True) diff --git a/botsort.yaml b/botsort.yaml new file mode 100644 index 0000000..922a5c0 --- /dev/null +++ b/botsort.yaml @@ -0,0 +1,18 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license +# Default YOLO tracker settings for BoT-SORT tracker https://github.com/NirAharon/BoT-SORT + +tracker_type: botsort # tracker type, ['botsort', 'bytetrack'] +track_high_thresh: 0.3 # threshold for the first association +track_low_thresh: 0.1 # threshold for the second association +new_track_thresh: 0.5 # threshold for init new track if the detection does not match any tracks +track_buffer: 10 # buffer to calculate the time when to remove tracks +match_thresh: 0.8 # threshold for matching tracks +# min_box_area: 10 # threshold for min box areas(for tracker evaluation, not used for now) +# mot20: False # for tracker evaluation(not used for now) + +# BoT-SORT settings +gmc_method: sparseOptFlow # method of global motion compensation +# ReID model related thresh (not supported yet) +proximity_thresh: 0.1 +appearance_thresh: 0.25 +with_reid: False \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 7d39345..068a16a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,6 +12,8 @@ services: runtime: nvidia environment: NVIDIA_VISIBLE_DEVICES: all + PYTHONUNBUFFERED: 1 + # OPENCV_FFMPEG_READ_ATTEMPTS: 16384 logging: driver: "json-file" options: diff --git a/ez.py b/ez.py new file mode 100644 index 0000000..b5e490e --- /dev/null +++ b/ez.py @@ -0,0 +1,40 @@ +import cv2 +import time + +# 获取 ezopen 协议的 URL +url = "rtmp://rtmp03open.ys7.com:1935/v3/openlive/L05874022_1_1?expire=1726487667&id=625419662355599360&t=228dca5d37b6bead201e80cca3fc5fe74140242278e4eb51a99bcce593cae027&ev=100" + +# 创建 VideoCapture 对象 +cap = cv2.VideoCapture(url) + +cv2.namedWindow("Video", cv2.WINDOW_NORMAL) # Create a named window +cv2.resizeWindow("Video", 640, 384) # Resize this window + +start_time = time.time() +count = 0 + +# 获取视频帧 +while True: + ret, frame = cap.read() + if time.time() - start_time < 1: # 循环运行直到1秒过去 + count += 1 + else: + print(count) + start_time = time.time() + count = 0 + + # print(ret) + if not ret: + print("Error read video stream.") + break + # 显示视频帧 + cv2.imshow("Video", frame) + # print("0000000000000",ret) + # 等待用户按下任意键退出 + key = cv2.waitKey(1) + if key == 27: + break + +# 释放资源 +cap.release() +cv2.destroyAllWindows() diff --git a/show.py b/show.py new file mode 100644 index 0000000..760001f --- /dev/null +++ b/show.py @@ -0,0 +1,245 @@ +import cv2 +import json +import time +import logging +import threading +import requests +import supervision as sv + +from datetime import datetime +from ultralytics import YOLO +from flask import Flask,request,Response +from multiprocessing import Process, Event + +app = Flask(__name__) + +@app.route('/show',methods=['POST']) +def show(): + # print(request.json) + data = request.json["content"] + balls = data["balls"] + table = data["table"] + text = f"table:{table:>3} " + for i in range(16): + ball = "#" if "ball"+str(i) in balls else " " + text += f"| {i} : {ball} " + print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),text) + return 'ok' +import re +@app.route('/mj',methods=['POST']) +def mj(): + print(request.json) + if request.json["progress"] == "100%": + print(request.json["imageUrl"]) + sendmsg(request.json["imageUrl"]) + download_image(request.json["imageUrl"], "MJ", request.json["state"]) + return 'ok' + +@app.route('/cq',methods=['POST']) +def cq(): + print(request.json) + if request.json["post_type"] != "message": + return 'ok' + if request.json["message_type"] != "group": + return 'ok' + if request.json["group_id"] != 116277572: + return 'ok' + # if request.json["user_id"] != 2132960: + # return 'ok' + + thread = threading.Thread(target=handle, args=(request.json,)) + thread.start() + return "ok" + +def handle(data): + print(data['raw_message']) + + # 正则表达式来匹配URL + url_pattern = re.compile(r'url=(.*?)\]') + + # 使用findall来找到所有的URL + urls = re.findall(url_pattern, data['raw_message']) + + result = re.sub(r'\[.*?\]', '', data['raw_message']) + result = result.replace('\r\n', '') + + name = data["sender"]["card"] or data["sender"]["nickname"] + + base64Array = [] + # 输出找到的URL + for url in urls: + print(url) + + download_image(url, "QQ", name) + # 从URL下载图片 + response = requests.get(url) + response.raise_for_status() # 如果请求失败,此处将引发异常 + + # 将图片数据转换为Base64编码的字节数组 + image_base64_bytes = base64.b64encode(response.content) + + # 如果您需要将其转换为字符串,您可以使用.decode('utf-8')将字节数组转换为字符串 + image_base64_str = image_base64_bytes.decode('utf-8') + + # 打印Base64编码的字节数组 + # print(image_base64_bytes) + base64Array.append('data:image/jpeg;base64,'+image_base64_str) + + if len(base64Array) == 0: + return 'ok' + prompt = 'game assets,game ui,animal,flower,plant,forest,cute,baby,magical,spirit,fairy,elf,glowing light,anime style,seeming very happy,from above,ultra detailed,soft,on white background' + if result != '': + prompt = result + # 示例调用 + params = { + 'base64Array':base64Array, + 'notifyHook': 'http://hk.luanhailiang.cn:5000/mj', + 'prompt': prompt, + 'state': name + } + + response = imagine(params) + print(response.json()) + time.sleep(3) + params['prompt'] = prompt + " --iw 2 --niji 5" + params['state'] = name+"_niji" + response = imagine(params) + print(response.json()) + time.sleep(3) + params['prompt'] = prompt + " --iw 2 --style raw" + params['state'] = name+"_raw" + response = imagine(params) + print(response.json()) + + return 'ok' + +def download_image(url, t, name): + """ + 从指定URL下载图片并保存到本地文件。 + + :param url: 图片的URL。 + :param local_filename: 要保存图片的本地文件名。 + """ + + local_filename = f"y:\美术\参考收集\{t}\{datetime.now().strftime('%Y%m%d%H%M%S')}_{name}" + response = requests.get(url) + if response.status_code == 200: # HTTP状态码200表示请求成功 + # 获取Content-Type响应头 + content_type = response.headers['Content-Type'] + # 判断图片格式 + ext = "" + if 'jpeg' in content_type or 'jpg' in content_type: + ext = '.jpg' + print('The image is in JPEG format.') + elif 'png' in content_type: + ext = '.png' + print('The image is in PNG format.') + elif 'gif' in content_type: + ext = '.gif' + print('The image is in GIF format.') + elif 'webp' in content_type: + ext = '.webp' + print('The image is in WEBP format.') + else: + ext = '.png' + print(f'Unknown image format: {content_type}') + local_filename += ext + with open(local_filename, 'wb') as f: + f.write(response.content) + print(f"Image successfully downloaded to {local_filename}") + else: + print(f"Error downloading image, HTTP status code: {response.status_code}") + + +import base64 +import requests + + +def imagine(params): + """ + 提交Imagine任务 + + :param params: imagineDTO,一个字典,包含以下键: + - base64Array: 垫图base64数组 + - notifyHook: 回调地址, 为空时使用全局notifyHook + - prompt: 提示词 + - state: 自定义参数 + :return: 返回服务器的响应 + """ + # API端点 + url = 'http://127.0.0.1:8080/mj/submit/imagine' + + # 发送POST请求 + response = requests.post(url, json=params) + + # 如果需要,处理响应(例如,检查响应状态,解析响应主体等) + + return response + + +def sendmsg(url): + # 设置 go-cqhttp 服务的 URL 和端口 + api_url = 'http://localhost:5700/send_msg' + + # 设置要发送的消息参数 + data = { + 'group_id': 5464741, # 接收消息的用户的 QQ 号 + 'message_type': 'group', # 消息类型 + 'message': f'[CQ:image,file={url}]' # 消息内容,使用 CQ码 格式 + } + + # 发送 POST 请求到 go-cqhttp 服务 + response = requests.post(api_url, data=data) + + # 输出响应 + print(response.json()) + +if __name__ == '__main__': + + # # 这是包含URL的示例文本 + # text = '[CQ:image,file=42c93be3dbe10bfa79ad93ce1baed561.image,subType=1,url=https://gchat.qpic.cn/gchatpic_new/1183464602/691534145-2408237736-42C93BE3DBE10BFA79AD93CE1BAED561/0?term=2&is_origin=0][CQ:image,file=42c93be3dbe10bfa79ad93ce1baed561.image,subType=1,url=https://gchat.qpic.cn/gchatpic_new/1183464602/691534145-2408237736-42C93BE3DBE10BFA79AD93CE1BAED561/0?term=2&is_origin=0]' + + # # 正则表达式来匹配URL + # url_pattern = re.compile(r'url=(.*?)\]') + + + + # # 使用findall来找到所有的URL + # urls = re.findall(url_pattern, text) + + # # 输出找到的URL + # for url in urls: + # print(url) + + + + # # 从URL下载图片 + # response = requests.get(url) + # response.raise_for_status() # 如果请求失败,此处将引发异常 + + # # 将图片数据转换为Base64编码的字节数组 + # image_base64_bytes = base64.b64encode(response.content) + + # # 如果您需要将其转换为字符串,您可以使用.decode('utf-8')将字节数组转换为字符串 + # image_base64_str = image_base64_bytes.decode('utf-8') + + # # 打印Base64编码的字节数组 + # # print(image_base64_bytes) + + # # 示例调用 + # params = { + # 'base64Array': ['data:image/jpeg;base64,'+image_base64_str], + # 'notifyHook': 'http://hk.luanhailiang.cn:5000/mj', + # 'prompt': 'character design, game assets, game ui, cute, soft', + # 'state': '' + # } + + # response = imagine(params) + # print(response.json()) + + + + + + app.logger.setLevel(logging.ERROR) + app.run("0.0.0.0",port=8000,threaded=True) diff --git a/test.py b/test.py index a712d35..f3cc25c 100644 --- a/test.py +++ b/test.py @@ -3,9 +3,10 @@ from urllib import parse from ultralytics import YOLO # Configure the tracking parameters and run the tracker -model = YOLO('best.pt') -source2 = "rtmp://rtmp03open.ys7.com:1935/v3/openlive/L05874022_1_1?expire=1726487667&id=625419662355599360&t=228dca5d37b6bead201e80cca3fc5fe74140242278e4eb51a99bcce593cae027&ev=100" +# model = YOLO('best.pt') +# source2 = "rtmp://rtmp03open.ys7.com:1935/v3/openlive/L05874022_1_1?expire=1726487667&id=625419662355599360&t=228dca5d37b6bead201e80cca3fc5fe74140242278e4eb51a99bcce593cae027&ev=100" # source = parse.urlencode({"source": source2}) # source = "./videos/123.mp4" -results = model.track(source=source2, conf=0.3, iou=0.5, show=True) - +# results = model.track(source=source2, conf=0.3, iou=0.5, show=True) +model = YOLO('best.pt') +results = model.track(source="./videos/123.mp4", tracker='botsort.yaml', show=True) diff --git a/videos/123123.mp4 b/videos/123123.mp4 new file mode 100644 index 0000000..619a13d --- /dev/null +++ b/videos/123123.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50f1d450d1e975b5a16160021777e2daf928db5ec5a90c9d7691a9a7cb3c7ad3 +size 15405787