feat: 完善智慧停车综合管理平台源码

This commit is contained in:
admin
2026-10-10 17:20:00 +08:00
commit 9c9079c998
170 changed files with 27985 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
__pycache__
+141
View File
@@ -0,0 +1,141 @@
name: pytorch
channels:
- pytorch
- nvidia
- https://repo.anaconda.com/pkgs/main/
- https://repo.anaconda.com/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- autopep8=1.6.0=pyhd3eb1b0_1
- blas=1.0=mkl
- brotlipy=0.7.0=py310h7f8727e_1002
- bzip2=1.0.8=h7b6447c_0
- ca-certificates=2023.01.10=h06a4308_0
- certifi=2022.12.7=py310h06a4308_0
- cffi=1.15.1=py310h5eee18b_3
- charset-normalizer=2.0.4=pyhd3eb1b0_0
- cryptography=39.0.1=py310h9ce1e76_0
- cuda-cudart=11.7.99=0
- cuda-cupti=11.7.101=0
- cuda-libraries=11.7.1=0
- cuda-nvrtc=11.7.99=0
- cuda-nvtx=11.7.91=0
- cuda-runtime=11.7.1=0
- ffmpeg=4.3=hf484d3e_0
- filelock=3.9.0=py310h06a4308_0
- flit-core=3.8.0=py310h06a4308_0
- freetype=2.12.1=h4a9f257_0
- giflib=5.2.1=h5eee18b_3
- gmp=6.2.1=h295c915_3
- gmpy2=2.1.2=py310heeb90bb_0
- gnutls=3.6.15=he1e5248_0
- idna=3.4=py310h06a4308_0
- intel-openmp=2021.4.0=h06a4308_3561
- jinja2=3.1.2=py310h06a4308_0
- jpeg=9e=h5eee18b_1
- lame=3.100=h7b6447c_0
- lcms2=2.12=h3be6417_0
- ld_impl_linux-64=2.38=h1181459_1
- lerc=3.0=h295c915_0
- libcublas=11.10.3.66=0
- libcufft=10.7.2.124=h4fbf590_0
- libcufile=1.6.0.25=0
- libcurand=10.3.2.56=0
- libcusolver=11.4.0.1=0
- libcusparse=11.7.4.91=0
- libdeflate=1.17=h5eee18b_0
- libffi=3.4.2=h6a678d5_6
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libiconv=1.16=h7f8727e_2
- libidn2=2.3.2=h7f8727e_0
- libnpp=11.7.4.75=0
- libnvjpeg=11.8.0.2=0
- libpng=1.6.39=h5eee18b_0
- libstdcxx-ng=11.2.0=h1234567_1
- libtasn1=4.19.0=h5eee18b_0
- libtiff=4.5.0=h6a678d5_2
- libunistring=0.9.10=h27cfd23_0
- libuuid=1.41.5=h5eee18b_0
- libwebp=1.2.4=h11a3e52_1
- libwebp-base=1.2.4=h5eee18b_1
- lz4-c=1.9.4=h6a678d5_0
- markupsafe=2.1.1=py310h7f8727e_0
- mkl=2021.4.0=h06a4308_640
- mkl-service=2.4.0=py310h7f8727e_0
- mkl_fft=1.3.1=py310hd6ae3a3_0
- mkl_random=1.2.2=py310h00e6091_0
- mpc=1.1.0=h10f8cd9_1
- mpfr=4.0.2=hb69a4c5_1
- ncurses=6.4=h6a678d5_0
- nettle=3.7.3=hbbd107a_1
- networkx=2.8.4=py310h06a4308_1
- numpy=1.23.5=py310hd5efca6_0
- numpy-base=1.23.5=py310h8e6c178_0
- openh264=2.1.1=h4ff587b_0
- openssl=1.1.1t=h7f8727e_0
- pillow=9.4.0=py310h6a678d5_0
- pip=23.0.1=py310h06a4308_0
- pycodestyle=2.10.0=py310h06a4308_0
- pycparser=2.21=pyhd3eb1b0_0
- pyopenssl=23.0.0=py310h06a4308_0
- pysocks=1.7.1=py310h06a4308_0
- python=3.10.10=h7a1cb2a_2
- pytorch=2.0.0=py3.10_cuda11.7_cudnn8.5.0_0
- pytorch-cuda=11.7=h778d358_3
- pytorch-mutex=1.0=cuda
- readline=8.2=h5eee18b_0
- requests=2.28.1=py310h06a4308_1
- setuptools=65.6.3=py310h06a4308_0
- six=1.16.0=pyhd3eb1b0_1
- sqlite=3.41.1=h5eee18b_0
- sympy=1.11.1=py310h06a4308_0
- tk=8.6.12=h1ccaba5_0
- toml=0.10.2=pyhd3eb1b0_0
- torchaudio=2.0.0=py310_cu117
- torchtriton=2.0.0=py310
- torchvision=0.15.0=py310_cu117
- typing_extensions=4.4.0=py310h06a4308_0
- urllib3=1.26.15=py310h06a4308_0
- wheel=0.38.4=py310h06a4308_0
- xz=5.2.10=h5eee18b_1
- zlib=1.2.13=h5eee18b_0
- zstd=1.5.4=hc292b87_0
- pip:
- aiohttp==3.8.4
- aiosignal==1.3.1
- async-timeout==4.0.2
- attrs==22.2.0
- bidict==0.22.1
- contourpy==1.0.7
- cycler==0.11.0
- fonttools==4.39.3
- frozenlist==1.3.3
- kiwisolver==1.4.4
- matplotlib==3.7.1
- mpmath==1.2.1
- multidict==6.0.4
- opencv-python==4.7.0.72
- packaging==23.0
- pandas==2.0.0
- psutil==5.9.4
- pyparsing==3.0.9
- python-dateutil==2.8.2
- python-engineio==4.4.0
- python-socketio==5.8.0
- pytz==2023.3
- pyyaml==6.0
- scipy==1.10.1
- seaborn==0.12.2
- sentry-sdk==1.19.1
- thop==0.1.1-2209072238
- tqdm==4.65.0
- tzdata==2023.3
- ultralytics==8.0.72
- websocket-client==1.5.1
- yarl==1.8.2
prefix: /root/anaconda3/envs/pytorch
+163
View File
@@ -0,0 +1,163 @@
from model import YOLOModel
from wsClient import WSClient
import asyncio
import multiprocessing
import os
import requests
import base64
import hashlib
import hmac
def getOfflineCameraIDs(httpServerUrl, adminUsername, password):
__SHA256KEY = b"smart-parking-platform"
r = requests.get(
httpServerUrl + "/api/ai/getOfflineCameraList",
params={
"adminUsername": adminUsername,
"password": base64.b64encode(
hmac.new(__SHA256KEY, password.encode("utf-8"), hashlib.sha256).digest()
).decode("utf-8"),
},
)
res = r.json()
if not res["success"]:
raise Exception(res["message"])
return list(map(lambda x: x["cameraID"], res["data"]))
# Windows推流本机摄像头到rtmp://localhost:1515/hls/3
# ffmpeg -list_devices true -f dshow -i dummy
# ffmpeg -f dshow -i video="USB2.0 HD UVC WebCam" -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -g 30 -f:v flv -c:a copy rtmp://192.169.3.3:1515/live/3
def ffmpegStreamToRtmpServer(streamUrl: str, rtmpUrl: str):
print("exec ffmpeg...")
if streamUrl.startswith("rtsp"):
os.execvp(
"/usr/bin/ffmpeg",
f"/usr/bin/ffmpeg -hide_banner -loglevel error -rtsp_transport tcp -i {streamUrl} -c:v libx264 -preset:v ultrafast -tune:v zerolatency -g 30 -f:v flv -c:a copy {rtmpUrl}".split(
" "
),
)
else:
os.execvp(
"/usr/bin/ffmpeg",
f"/usr/bin/ffmpeg -hide_banner -loglevel error -i {streamUrl} -c:v libx264 -preset:v ultrafast -tune:v zerolatency -g 30 -f:v flv -c:a copy {rtmpUrl}".split(
" "
),
)
pass
async def beginWork(ws: WSClient):
rtmpUrl = ws.rtmpServerUrl + "/" + ws.cameraID
ffmpegProcess = multiprocessing.Process(
target=ffmpegStreamToRtmpServer, args=(ws.rtspUrl, rtmpUrl), daemon=True
)
try:
model = YOLOModel(device=ws.context.get("modelDevice", "cpu"))
ffmpegProcess.start()
print(
f"Begin to detect video for camera {ws.cameraID}, streamUrl: {ws.rtspUrl} \nIf wait too long, please check if the stream url is correct."
)
results = model.detectVideo(ws.rtspUrl, classList=[0, 2]) # 0:person, 2:car
for frameResult in results:
clsCount = model.getResultClsCount(frameResult)
await ws.trySendAlarm(
{
"algorithmType": "body",
"count": clsCount.get("person", 0),
"predictResult": frameResult,
}
)
await ws.trySendAlarm(
{
"algorithmType": "vehicle",
"count": clsCount.get("car", 0),
"predictResult": frameResult,
}
)
if not ffmpegProcess.is_alive():
print(f"ffmpeg process for camera {ws.cameraID} is dead")
break
await asyncio.sleep(float(ws.context.get("interval", 1)))
pass
finally:
ffmpegProcess.kill()
await ws.disconnect()
print("error, kill ffmpeg process")
pass
def main(
wsServerUrl="",
rtmpServerUrl="",
adminUsername="",
password="",
cameraID="",
detectionInterval=0.5,
modelDevice="cpu",
):
asyncio.run(
WSClient(
wsServerUrl,
rtmpServerUrl,
adminUsername,
password,
str(cameraID),
beginWork,
{"interval": float(detectionInterval), "modelDevice": modelDevice},
).stayConnected()
)
pass
if __name__ == "__main__":
multiprocessing.freeze_support()
httpServerUrl = os.getenv("HTTP_SERVER_URL", "http://localhost")
wsServerUrl = httpServerUrl + "/ws"
rtmpServerUrl = os.getenv("RTMP_SERVER_URL", "rtmp://localhost:1515/live")
adminUsername = os.getenv("ADMIN_USERNAME", "admin123")
password = os.getenv("ADMIN_PASSWORD", "admin123")
cameraIDs = []
if os.getenv("CAMERA_IDS") is not None:
cameraIDs = os.getenv("CAMERA_IDS").split(",")
else:
cameraIDs = getOfflineCameraIDs(httpServerUrl, adminUsername, password)
detectionInterval = float(os.getenv("DETECTION_INTERVAL", 0.5))
modelDevice = os.getenv("MODEL_DEVICE", "cpu")
processes = []
# start a process for each camera
for cameraID in cameraIDs:
p = multiprocessing.Process(
target=main,
args=(
wsServerUrl,
rtmpServerUrl,
adminUsername,
password,
cameraID,
detectionInterval,
modelDevice,
),
)
p.start()
processes.append(p)
pass
# wait for all processes to finish
for p in processes:
p.join()
pass
+51
View File
@@ -0,0 +1,51 @@
from ultralytics import YOLO
class YOLOModel:
__model = None
conf = 0.5
device = "cpu"
def __init__(self, conf=0.5, device="cpu"):
self.conf = conf
self.device = device
self.__model = YOLO(model="./yolov8n.pt", task="detect")
def detectImage(self, image, classList=[0, 2]):
result = self.__model.predict(
source=image,
verbose=False,
conf=self.conf,
line_thickness=1,
classes=classList,
device=self.device,
)[0]
result.clsCount = self.getResultClsCount(result)
return result
def detectVideo(self, video, classList=[0, 2]):
result = self.__model.predict(
source=video,
verbose=False,
stream=True,
line_thickness=1,
conf=self.conf,
classes=classList,
device=self.device,
)
return result
def getResultClsCount(self, result):
clsCount = dict()
detectedCls = result.boxes.cls
for clsValue in detectedCls.unique():
clsName = str(result.names[int(clsValue)])
count = int((detectedCls == clsValue).sum())
clsCount[clsName] = count
return clsCount
def getSupportedClsList(self):
return self.__model.names
pass
+213
View File
@@ -0,0 +1,213 @@
from typing import Any
import socketio
import hashlib
import hmac
import base64
import json
import cv2
import asyncio
from datetime import datetime
class WSClient:
__SHA256KEY = b"smart-parking-platform"
sio = socketio.AsyncClient(reconnection=False)
wsServerUrl = None
rtmpServerUrl = None
connected = False
ready = False
async def onReady(self):
pass
username = None
password = None
cameraID = None
rtspUrl = None
alarmRules = None
"""
@type alarmRule: [{
'id': 1,
'name': '测试规则',
'enabled': True,
'algorithmType':'body',
'triggerDayOfWeek': [1, 2, 3, 4, 5],
'triggerTimeStart': '09:00:00',
'triggerTimeEnd': '18:00:00',
'triggerCountMin': 0,
'triggerCountMax': -1
}]
"""
alarmThrottle = {}
"""
@type alarmThrottle: {
[ alarmRuleID:str ]: datetime
}
"""
throttleTime = 60 # seconds
context = {}
def __init__(
self,
wsServerUrl: str,
rtmpServerUrl: str,
username: str,
password: str,
cameraID: str,
onReady: Any,
context: dict = {},
) -> None:
self.wsServerUrl = wsServerUrl
self.rtmpServerUrl = rtmpServerUrl
self.username = username
self.password = base64.b64encode(
hmac.new(
self.__SHA256KEY, password.encode("utf-8"), hashlib.sha256
).digest()
).decode("utf-8")
self.cameraID = cameraID
self.onReady = onReady
self.context = context
self.sio.on("cameraConfigChange", self.onCameraConfigChange)
self.sio.on("connect", self.onConnect)
self.sio.on("disconnect", self.onDisconnect)
self.sio.on("connect_error", self.onConnectError)
print(f"ws client for camera {cameraID} created")
pass
async def connect(self):
if self.connected:
return
data = json.dumps(
{
"username": self.username,
"password": self.password,
"cameraID": self.cameraID,
}
)
await self.sio.connect(
self.wsServerUrl, socketio_path="/ws/ai/", headers={"data": data}
)
self.connected = True
pass
async def sendMessage(self, msg):
await self.sio.emit("message", msg)
print("message sent")
pass
def matchAlarmRule(self, data):
"""
@type data: {
'algorithmType': 'body',
'count': 1,
'predictResult': Any
}
"""
matchedRules = []
if not self.ready:
raise Exception(f"CameraID {self.cameraID} not ready or disconnected")
if self.alarmRules is None:
return matchedRules
for rule in self.alarmRules:
if (
rule["enabled"]
and rule["algorithmType"] == data["algorithmType"]
and (datetime.now().weekday() + 1) in rule["triggerDayOfWeek"]
and datetime.now().time()
>= datetime.strptime(rule["triggerTimeStart"], "%H:%M:%S").time()
and datetime.now().time()
<= datetime.strptime(rule["triggerTimeEnd"], "%H:%M:%S").time()
and data["count"] >= rule["triggerCountMin"]
and (
data["count"] <= rule["triggerCountMax"]
or rule["triggerCountMax"] == -1
)
and (
self.alarmThrottle.get(rule["id"]) is None
or (datetime.now() - self.alarmThrottle[rule["id"]]).seconds
> self.throttleTime
)
):
matchedRules.append(rule)
return matchedRules
async def trySendAlarm(self, data):
"""
@type data: {
'algorithmType': 'body',
'count': 1,
'predictResult': Any
}
"""
rules = self.matchAlarmRule(data)
for rule in rules:
"""
AlarmData: {
picBase64: string;
alarmRuleID: number;
}
"""
await self.sio.emit(
"alarm",
{
"alarmRuleID": rule["id"],
"picBase64": "data:image/jpg;base64,"
+ base64.b64encode(
cv2.imencode(".jpg", data["predictResult"].plot())[1]
).decode("utf-8"),
},
)
# 防止短时间内重复发送
self.alarmThrottle[rule["id"]] = datetime.now()
print(f"camera {self.cameraID} alarm sent: {rule['name']}")
pass
pass
async def disconnect(self):
await self.sio.disconnect()
pass
async def onCameraConfigChange(self, data):
if self.rtspUrl is not None and self.rtspUrl != data["rtspUrl"]:
print(f"Camera {self.cameraID} rtsp url changed, disconnecting...")
await self.disconnect()
return
self.rtspUrl = data["rtspUrl"]
self.alarmRules = data["alarmRules"]
print(f"Camera {self.cameraID} config updated")
if self.connected and not self.ready:
self.ready = True
asyncio.get_event_loop().create_task(self.onReady(self))
pass
async def stayConnected(self):
await self.connect()
await self.sio.wait()
pass
def onConnect(self):
print(f"connection for camera {self.cameraID} established")
pass
def onDisconnect(self):
print(f"camera {self.cameraID} disconnected from server")
self.connected = False
self.ready = False
pass
def onConnectError(self, err):
print(err)
pass
BIN
View File
Binary file not shown.