TitanWong 2 years ago
commit
e6c4efb446
8 changed files with 293 additions and 0 deletions
  1. 13 0
      .drone.yml
  2. 15 0
      Dockerfile
  3. 22 0
      README.md
  4. 10 0
      config.yaml
  5. 223 0
      cvControl.py
  6. BIN
      exchange_new.mp4
  7. 10 0
      prod/config.yaml
  8. BIN
      script1.xlsx

+ 13 - 0
.drone.yml

@@ -0,0 +1,13 @@
+kind: pipeline
+name: default
+
+steps:
+  - name: docker
+    image: plugins/docker
+    settings:
+      tags:
+        - latest
+        - 1.0.${DRONE_BUILD_NUMBER}
+      registry: nas.fast-fun.cn:5000
+      insecure: true
+      repo: nas.fast-fun.cn:5000/screen/robotcv

+ 15 - 0
Dockerfile

@@ -0,0 +1,15 @@
+FROM nas.fast-fun.cn:5000/ffmpeg
+
+
+ENV TZ="Asia/Shanghai" QT_DEBUG_PLUGINS=1
+WORKDIR /home/project
+
+COPY *.py /home/project/
+COPY *.yaml /home/project/
+COPY *.mp4 /home/project/
+COPY *.xlsx /home/project/
+ADD  ./prod /home/project/prod
+
+
+# ENTRYPOINT /usr/bin/python3
+CMD ["/bin/sh","-c","/usr/bin/python3 cvControl.py /home/project/prod/"]

+ 22 - 0
README.md

@@ -0,0 +1,22 @@
+运行环境:
+    python 3.7
+依赖包:
+    opencv-python
+    pandas
+    pyyaml
+操作系统环境:
+    需要安装ffmpeg,并且path中要有运行路径。
+
+说明:
+1. config.yaml是配置文件,里面配置了程序正常启动所需的参数, 可以将config.yaml拷贝到任意目录path(例如/a/b/),然后用python cvControl.py path(例如python cvControl.py /a/b)来启动程序。
+
+# 视频中继地址
+relay_url: http://127.0.0.1:8081/video1
+# 通知地址
+post_url: http://127.0.0.1:8090/sync
+# 脚本excel绝对地址
+script_excel: E:\rlprojects\pyproj\exchange_bs\script1.xlsx
+# 视频文件绝对地址
+video_file: E:\rlprojects\pyproj\exchange_bs\exchange_new.mp4
+# 调试视频,部署的时候为false
+debug_video: false

+ 10 - 0
config.yaml

@@ -0,0 +1,10 @@
+# 视频中继地址
+relay_url: http://127.0.0.1:8081/video1
+# 通知地址
+post_url: http://127.0.0.1:8090/sync
+# 脚本excel绝对地址
+script_excel: E:\rlprojects\cvcontrol\script1.xlsx
+# 视频文件绝对地址
+video_file: E:\rlprojects\cvcontrol\exchange_new.mp4
+# 调试视频
+debug_video: false

+ 223 - 0
cvControl.py

@@ -0,0 +1,223 @@
+import subprocess as sp
+import time
+import cv2
+import requests
+import pandas as pd
+import json
+from threading import Thread
+from queue import Queue
+import math
+import yaml
+import sys
+import os
+import datetime
+import dateutil.relativedelta
+
+# 默认当前目录
+config_dir = os.path.dirname(os.path.abspath(__file__))
+# 有配置目录参数,取配置的目录
+args = sys.argv
+print(args)
+if len(args)>1:
+    config_dir=args[1]
+
+config_file_path = os.path.join(config_dir, "config.yaml")
+config_file = open(config_file_path, "r", encoding="utf-8")
+configs = yaml.load(config_file)
+print(configs)
+
+# 实时图像中继url
+relay_url = configs["relay_url"]
+
+# 数据通知url
+post_url = configs["post_url"]
+# 脚本excel
+script_excel = configs["script_excel"]
+# 视频文件
+video_file = configs["video_file"]
+# 开启调试视频
+debug_video = configs["debug_video"]
+# 读取脚本文件
+dfs = pd.read_excel(script_excel, sheet_name=["script","user","reset"])
+df = dfs.get("script")
+udf = dfs.get("user")
+rdf = dfs.get("reset")
+records = df.to_dict("records")
+users = udf.to_dict("records")
+resets = rdf.to_dict("records")[0]
+# print(resets)
+# print("read: {} records".format(len(records)))
+script = {}
+for i in range(len(records)):
+    # print(records[i]["userInfo"])
+    ts = records[i]["time"]
+    p = records[i]["process"].replace("\n","").replace("\t","") if not pd.isnull(records[i]["process"]) else None
+    a = records[i]["carAnimation"].replace("\n","").replace("\t","") if not pd.isnull(records[i]["carAnimation"]) else None
+    u = True if not pd.isnull(records[i]["userInfo"]) else False
+    # if u:
+    #     print("----------")
+    #     print(records[i]["time"],u)
+    h,m,s,ms = ts.split("-")
+    h,m,s,ms = int(h),int(m),int(s),int(ms)
+    ts_1 = ms+s*1000+m*1000*60+h*1000*60*60
+    # 每秒60帧
+    offsetFrames = int(ts_1*30/1000)
+    # print(i, p, a)
+    d = {"ts":ts}
+    if p:
+        d.update({"process": json.loads(p)})
+    if a:
+        d.update({"carAnimation": json.loads(a)})
+    d.update({"userInfo": u})
+    script[offsetFrames] = d
+rps = resets["process"].replace("\n","").replace("\t","") if not pd.isnull(resets["process"]) else None
+ras = resets["carAnimation"].replace("\n","").replace("\t","") if not pd.isnull(resets["carAnimation"]) else None
+rpds = resets["positionData"].replace("\n","").replace("\t","") if not pd.isnull(resets["carAnimation"]) else None
+
+rd = {"ts":ts}
+if rps:
+    rd.update({"process": json.loads(rps)})
+if ras:
+    rd.update({"carAnimation": json.loads(ras)})
+if rpds:
+    rd.update({"positionData": json.loads(rpds)})
+# print(rd)
+# exit(0)
+
+
+
+queue = Queue()
+
+# 定义通知worker的线程函数
+def worker(q):
+    user_index = 0
+    action = {"type":"reset", "data":{"process":rd["process"], "carAnimation":rd["carAnimation"], "positionData":rd["positionData"], "t":rd["ts"]}}
+    try:
+        re = requests.post(post_url, json = action)
+        print("-------action:", action)
+        print(re)
+    except Exception as e:
+        print("Error:")
+        print(e)
+    while True:
+        actions = q.get()
+        t, p, a, u=None,None,None,False
+        t = actions.get("ts", None)
+        p = actions.get("process", None)
+        a = actions.get("carAnimation", None)
+        u = actions.get("userInfo", False)
+        # 控制换电流程状态
+        if p:
+            action = {"type":"process", "data":{"process":p, "t":t}}
+            try:
+                re = requests.post(post_url, json = action)
+                print("-------action:", action)
+                print(re)
+            except Exception as e:
+                print("Error:")
+                print(e)
+        # 控制动画 
+        if a:
+            action = {"type":"carAnimation", "data":{"carAnimation":a, "t":t}}
+            try:
+                re = requests.post(post_url, json = action)
+                print("-------action:", action)
+                print(re)
+            except Exception as e:
+                print("Error:")
+                print(e)
+        # 用户信息推送 
+        if u:
+            user = users[user_index]
+            # print("---user:", user)
+            action = {"type": "baseInfo", "data":{"baseInfo":user}}
+            try:
+                re = requests.post(post_url, json = action)
+                print("-------action:", action)
+                print(re)
+            except Exception as e:
+                print("Error:")
+                print(e)
+            user_index = (user_index+1) % len(users)
+
+t = Thread(target=worker,args=(queue,))
+t.start()
+
+
+# 打开视频
+cap=cv2.VideoCapture(video_file)
+# 总帧数
+total_frame_cnt = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
+# fps
+fps = int(cap.get(cv2.CAP_PROP_FPS))
+
+# 执行ffmpeg管道命令
+command= [
+    'ffmpeg',
+    '-f', 'rawvideo',
+    '-vcodec', 'rawvideo',
+    '-pix_fmt', 'bgr24',
+    '-s', '512x288',
+    '-r','30',
+    '-i', '-',
+    '-r','30',
+    '-q', '0',
+    '-codec:v', 'mpeg1video',
+    '-f', 'mpegts',
+    relay_url
+]
+pipe = sp.Popen(command,stdin=sp.PIPE)
+# exit(0)
+# 每帧间隔 ms
+delay = 1000/fps
+wait = round(delay/1000.,2)
+print("-----------", total_frame_cnt, fps, delay,wait)
+
+# 启动推流子进程,不断推流
+
+time.sleep(1)
+
+frame_counter = 0
+while (cap.isOpened()):
+    print("+++++++++++++", frame_counter)
+    ret, frame = cap.read()
+    if not ret:
+        print("not ret")
+        break
+    
+    frame = cv2.resize(frame, (512, 288))
+    # 1. 图像写入管道
+    # 模n采样
+    if frame_counter%3==0:
+        nt = datetime.datetime.now()
+        nt+=dateutil.relativedelta.relativedelta(seconds=-2)
+        timeStr = nt.strftime('%Y-%m-%d %H:%M:%S')
+        cv2.putText(frame, timeStr, (10, 20), cv2.FONT_ITALIC, .4, (255, 255, 255), 1)
+        try:
+            pipe.stdin.write(frame.tostring())
+        except Exception as e:
+            print("may be broken pipe")
+            print(e)
+            break
+    actions = script.get(frame_counter, None)
+    
+    if actions:
+        queue.put(actions)
+    # print("---------",wait)
+    # print(frame_counter,"+++++++++++",delay, waste, wait)
+    if debug_video:
+        cv2.imshow('show', frame)
+        if cv2.waitKey(math.ceil(delay*0.5)) == ord('q'):  # 按Q退出
+            break
+    else:
+        time.sleep(delay*0.5/1000)
+    # time.sleep(wait)
+    frame_counter += 1
+    if frame_counter >= total_frame_cnt:
+        frame_counter = 0
+        cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
+        
+print("break out")
+cap.release()
+pipe.terminate()
+t.join()

BIN
exchange_new.mp4


+ 10 - 0
prod/config.yaml

@@ -0,0 +1,10 @@
+# 视频中继地址
+relay_url: http://120.27.243.131:9094/video1
+# 通知地址
+post_url: http://120.27.243.131:9093/sync
+# 脚本excel绝对地址
+script_excel: /home/project/script1.xlsx
+# 视频文件绝对地址
+video_file: /home/project/exchange_new.mp4
+# 调试视频
+debug_video: false

BIN
script1.xlsx