Compare commits

..
15 Commits
Author SHA1 Message Date
feie9454 c099c90aad remove web build dist 2025-09-01 14:32:49 +08:00
feie9454 fd0e468db9 edit web page 2025-08-20 12:27:06 +08:00
feie9454 b343ef2ddd add sys service 2025-08-20 12:21:57 +08:00
feie9454 53e704e0bf add static web files 2025-08-20 12:08:25 +08:00
feie9454 4b9374fcdc edit project arch 2025-08-20 09:08:20 +08:00
碳灰面包 9d2402d278 version 0.12
model_2.pt 是专门用来演示的模型
body_congition.py 是识别人体骨骼的
2025-08-10 22:24:54 +08:00
碳灰面包 d921497253 Revert "version 0.11.1"
This reverts commit e396c59d50.
2025-08-09 15:37:37 +08:00
碳灰面包 e396c59d50 version 0.11.1
model_2.pt 是专门用来演示的模型
2025-08-09 13:39:47 +08:00
碳灰面包 a1da9df94d version 0.12
返回的json里新增元件框数据
2025-06-22 15:59:16 +08:00
碳灰面包 ff3f886270 Merge remote-tracking branch 'circuit_recognition/master' 2025-06-18 22:53:42 +08:00
feie9454 7d798195cb fix sysnax err 2025-06-18 22:51:17 +08:00
碳灰面包 d0e5885578 version 0.11
修改了电表的type
2025-06-18 22:51:16 +08:00
碳灰面包 6ada6e7944 version 0.11
修改了电表的type
2025-06-18 22:44:40 +08:00
feie9454 b189f96b8d modify default resistance 2025-06-18 21:05:05 +08:00
feie9454 0521992bf2 init uv 2025-06-18 19:58:16 +08:00
15 changed files with 1359 additions and 30 deletions
-1
View File
@@ -133,7 +133,6 @@ dmypy.json
data/ data/
datasets/ datasets/
*.csv *.csv
*.json
*.xml *.xml
*.txt *.txt
# Image files (uncomment if you have large image datasets) # Image files (uncomment if you have large image datasets)
View File
+63
View File
@@ -0,0 +1,63 @@
import cv2
import mediapipe as mp
import csv
import os
VIDEO_PATH = r"E:\code\tmp\test.mp4" # 视频文件路径
# 检查视频文件存在
if not os.path.exists(VIDEO_PATH):
raise FileNotFoundError(f"视频文件未找到: {VIDEO_PATH}")
# 初始化 MediaPipe Pose 模块
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils
pose = mp_pose.Pose(
static_image_mode=False,
model_complexity=1,
enable_segmentation=False,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
# 打开视频文件
cap = cv2.VideoCapture(VIDEO_PATH)
if not cap.isOpened():
raise RuntimeError(f"无法打开视频文件: {VIDEO_PATH}")
while True:
success, frame = cap.read()
if not success:
break # 视频播放完毕
# 可以调整窗口
frame = cv2.resize(frame, (1920, 1080))
# 转为RGB格式
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image_rgb.flags.writeable = False
# 骨架识别
results = pose.process(image_rgb)
# 绘制骨架图像
image_rgb.flags.writeable = True
image_bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR)
if results.pose_landmarks:
mp_drawing.draw_landmarks(
image=image_bgr,
landmark_list=results.pose_landmarks,
connections=mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2),
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 0, 0), thickness=2)
)
# 显示画面
cv2.imshow("跳水视频骨架识别(MediaPipe)", image_bgr)
key = cv2.waitKey(1)
if key == 27: # 按Esc键退出
break
cap.release()
cv2.destroyAllWindows()
+26
View File
@@ -0,0 +1,26 @@
[Unit]
Description=Circuit Recognition Web Service
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/home/feie9454/elements_wires_congition
Environment=PATH=/home/feie9454/elements_wires_congition/venv/bin
ExecStart=/home/feie9454/elements_wires_congition/venv/bin/python main.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=circuit-recognition
# 安全设置
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/home/feie9454/elements_wires_congition
[Install]
WantedBy=multi-user.target
+57 -4
View File
@@ -1,12 +1,54 @@
from flask import Flask, request, jsonify from flask import Flask, request, jsonify, send_from_directory, send_file
from flask_cors import CORS from flask_cors import CORS
import numpy as np import numpy as np
import cv2 import cv2
from img_prec import img_recognition import os
app = Flask(__name__) from src.pre_func.img_prec import img_recognition
# 设置静态文件目录
app = Flask(__name__, static_folder='src/web', static_url_path='/static')
CORS(app) CORS(app)
@app.route('/')
def index():
"""主页路由,提供index.html"""
return send_from_directory(app.static_folder, 'index.html')
@app.route('/web')
def web_index():
"""Web应用入口"""
return send_from_directory(app.static_folder, 'index.html')
@app.route('/web/<path:filename>')
def web_static(filename):
"""提供web目录下的静态文件"""
return send_from_directory(app.static_folder, filename)
@app.route('/static/<path:filename>')
def static_files(filename):
"""提供静态文件服务"""
return send_from_directory(app.static_folder, filename)
@app.route('/api/status')
def api_status():
"""API状态检查"""
return jsonify({
'status': 'running',
'message': 'Elements Wires Recognition API is running',
'endpoints': {
'POST /process_image': 'Image recognition endpoint',
'GET /': 'Main web interface',
'GET /web': 'Web application',
'GET /api/status': 'API status check'
}
})
@app.route('/process_image', methods=['POST']) @app.route('/process_image', methods=['POST'])
def process_image(): def process_image():
if 'image' not in request.files: if 'image' not in request.files:
@@ -24,4 +66,15 @@ def process_image():
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True, port=8000) print("🚀 Starting Elements Wires Recognition Web Server...")
print("📁 Static files served from: src/web/")
print("🌐 Web interface available at:")
print(" - http://localhost:8000/")
print(" - http://0.0.0.0:8000/")
print("🔧 API endpoints:")
print(" - POST /process_image (for image recognition)")
print(" - GET /web/<filename> (for static files)")
print(" - GET /static/<filename> (for static files)")
print("=" * 50)
app.run(host='0.0.0.0', debug=True, port=12399)
+63
View File
@@ -0,0 +1,63 @@
#!/bin/bash
# Circuit Recognition Service Management Script
# 使用方法: ./manage_service.sh [install|start|stop|restart|status|logs|uninstall]
SERVICE_NAME="circuit-recognition"
SERVICE_FILE="/home/feie9454/elements_wires_congition/circuit-recognition.service"
SYSTEM_SERVICE_PATH="/etc/systemd/system/circuit-recognition.service"
case "$1" in
install)
echo "安装服务..."
sudo cp "$SERVICE_FILE" "$SYSTEM_SERVICE_PATH"
sudo systemctl daemon-reload
sudo systemctl enable "$SERVICE_NAME"
echo "✅ 服务已安装并设置为开机自启"
echo "使用 './manage_service.sh start' 启动服务"
;;
start)
echo "启动服务..."
sudo systemctl start "$SERVICE_NAME"
echo "✅ 服务已启动"
;;
stop)
echo "停止服务..."
sudo systemctl stop "$SERVICE_NAME"
echo "✅ 服务已停止"
;;
restart)
echo "重启服务..."
sudo systemctl restart "$SERVICE_NAME"
echo "✅ 服务已重启"
;;
status)
echo "服务状态:"
sudo systemctl status "$SERVICE_NAME"
;;
logs)
echo "查看服务日志:"
sudo journalctl -u "$SERVICE_NAME" -f
;;
uninstall)
echo "卸载服务..."
sudo systemctl stop "$SERVICE_NAME"
sudo systemctl disable "$SERVICE_NAME"
sudo rm -f "$SYSTEM_SERVICE_PATH"
sudo systemctl daemon-reload
echo "✅ 服务已卸载"
;;
*)
echo "使用方法: $0 {install|start|stop|restart|status|logs|uninstall}"
echo ""
echo "命令说明:"
echo " install - 安装服务并设置开机自启"
echo " start - 启动服务"
echo " stop - 停止服务"
echo " restart - 重启服务"
echo " status - 查看服务状态"
echo " logs - 实时查看服务日志"
echo " uninstall - 卸载服务"
exit 1
;;
esac
+14
View File
@@ -0,0 +1,14 @@
[project]
name = "circuit-recognition"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"flask>=3.1.1",
"flask-cors>=6.0.1",
"numpy>=2.3.0",
"opencv-python>=4.11.0.86",
"scikit-image>=0.25.2",
"ultralytics>=8.3.156",
]
+1
View File
@@ -0,0 +1 @@
# 这个文件使 src 目录成为一个 Python 包
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
# 这个文件使 pre_func 目录成为一个 Python 包
+7 -1
View File
@@ -1,15 +1,21 @@
import cv2 import cv2
import os
from ultralytics import YOLO from ultralytics import YOLO
def elements_recognition(img): def elements_recognition(img):
model = YOLO('best_model/best.pt') # 获取当前文件的目录,然后构建模型文件的绝对路径
current_dir = os.path.dirname(os.path.abspath(__file__))
model_path = os.path.join(current_dir, '../best_model/model_2.pt')
model = YOLO(model_path)
original = img original = img
img = cv2.resize(original, (1000, int(original.shape[0] * 1000 / original.shape[1]))) img = cv2.resize(original, (1000, int(original.shape[0] * 1000 / original.shape[1])))
results = model(img)[0] results = model(img)[0]
components = [] components = []
for box in results.boxes: for box in results.boxes:
if box.conf < 0.6:
continue
cls = int(box.cls[0]) cls = int(box.cls[0])
label = model.names[cls] label = model.names[cls]
x1, y1, x2, y2 = map(int, box.xyxy[0]) x1, y1, x2, y2 = map(int, box.xyxy[0])
+49 -23
View File
@@ -1,12 +1,11 @@
import json
import datetime import datetime
import wires_recog from . import wires_recog
import ele_recog from . import ele_recog
import cv2 import cv2
import time
import numpy as np import numpy as np
import base64 import base64
# 生成json数据 # 生成json数据
def generate_json(wires, components): def generate_json(wires, components):
vertices = [] vertices = []
@@ -19,7 +18,13 @@ def generate_json(wires, components):
"电阻箱": "RESISTOR_BOX", "电阻箱": "RESISTOR_BOX",
"滑动变阻器": "VARIABLE_RESISTOR", "滑动变阻器": "VARIABLE_RESISTOR",
"单刀双掷开关": "switch", "单刀双掷开关": "switch",
"电源": "battery" "单刀单掷开关": "switch",
"灯泡": "lightBulb",
"电源": "battery",
"电池电源": "battery",
"电阻": "RESISTOR",
"黑盒电流表": "ammeter",
"螺线管": "solenoid"
} }
for comp in components: for comp in components:
@@ -69,14 +74,30 @@ def generate_json(wires, components):
if label == "switch": if label == "switch":
elem["closed"] = False elem["closed"] = False
elif label == "ammeter": elif label == "ammeter":
elem["internalResistance"] = 0.01 elem["resistance"] = 1
elif label == "RESISTOR_BOX" or label == "VARIABLE_RESISTOR": elem["type"] = "seriesAmmeter"
elem["customLabel"] = "电流表"
elem["customDisplayFunction"] = "i => `${i.toFixed(2)} A`"
elif label == "voltmeter":
elem["resistance"] = 1
elem["type"] = "seriesAmmeter"
elem["customLabel"] = "电压表"
elem["customDisplayFunction"] = "i => `${i.toFixed(2)} V`"
elif label == "RESISTOR_BOX" or label == "VARIABLE_RESISTOR" or label == "RESISTOR":
elem["type"] = "resistor" elem["type"] = "resistor"
elem["resistorType"] = label elem["resistorType"] = label
elem["resistance"] = 10
elif label == "battery": elif label == "battery":
elem["voltage"] = 9 elem["voltage"] = 9
elem["batterType"] = "BATTERRY" elem["type"] = "battery"
elem["internalResistance"] = 0.0001 elem["batteryType"] = "BATTERY"
elem["internalResistance"] = 0.01
elif label == "resistor":
elem["resistorType"] = "RESISTOR"
elem["resistance"] = 1
elif label == "lightBulb":
elem["resistance"] = 10
elem["isReal"] = False
elements.append(elem) elements.append(elem)
element_count += 1 element_count += 1
@@ -165,24 +186,30 @@ def visualize_wires_and_components(image, results, components):
"电阻箱": "RESISTOR_BOX", "电阻箱": "RESISTOR_BOX",
"滑动变阻器": "VARIABLE_RESISTOR", "滑动变阻器": "VARIABLE_RESISTOR",
"单刀双掷开关": "switch", "单刀双掷开关": "switch",
"电源": "BATTERY" "单刀单掷开关": "switch",
"灯泡":"lightBulb",
"电源": "battery",
"电池电源": "battery",
"电阻": "RESISTOR",
"黑盒电流表": "ammeter",
"螺线管": "solenoid"
} }
for comp in components: for comp in components:
label = labels[comp["label"]] label = labels[comp["label"]]
x1, y1, x2, y2 = comp["bbox"] x1, y1, x2, y2 = comp["bbox"]
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 128, 0), 2)
cv2.putText(img, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 128, 0), 2)
_, encode_img = cv2.imencode('.jpg', img) _, encode_img = cv2.imencode('.jpg', img)
img_base64 = base64.b64encode(encode_img).decode('utf-8') img_base64 = base64.b64encode(encode_img).decode('utf-8')
#cv2.imwrite('output.jpg', img) # cv2.imwrite('output.jpg', img)
# cv2.namedWindow("Wires and Components", cv2.WINDOW_NORMAL)
# cv2.imshow("Wires and Components", img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
return img_base64 return img_base64
# 显示图像 # 显示图像
# resized = cv2.resize(img, (0, 0), fx=0.6, fy=0.6) # resized = cv2.resize(img, (0, 0), fx=0.6, fy=0.6)
# cv2.namedWindow("Wires and Components", cv2.WINDOW_NORMAL)
# cv2.imshow("Wires and Components", resized)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
def img_recognition(img): def img_recognition(img):
@@ -204,7 +231,8 @@ def img_recognition(img):
request = { request = {
"success": True, "success": True,
"recognizedImage": f"data:image/jpeg;base64,{results_img}", "recognizedImage": f"data:image/jpeg;base64,{results_img}",
"circuitData": results "circuitData": results,
"components": elements
} }
# with open('test.json', "w") as f: # with open('test.json', "w") as f:
# json.dump(request, f, indent=2) # json.dump(request, f, indent=2)
@@ -214,10 +242,8 @@ def img_recognition(img):
# if __name__ == '__main__': # if __name__ == '__main__':
# start = time.perf_counter() # start = time.perf_counter()
# imgs_path = [ # imgs_path = [r"E:\code\tmp\mmexport1754723780254.jpg"]
# # img = cv2.imread(imgs_path[0])
# ] # img_recognition(img)
# for img_path in imgs_path:
# img_recognition(img_path)
# end = time.perf_counter() # end = time.perf_counter()
# print(f"处理{len(imgs_path)}张图片耗时:{end - start:.2f}s") # print(f"处理{len(imgs_path)}张图片耗时:{end - start:.2f}s")
@@ -55,7 +55,8 @@ def detect_wires_and_endpoints(image):
for color_name, hsv_ranges in ranges.items(): for color_name, hsv_ranges in ranges.items():
# print(f"\n🟢 正在处理颜色: {color_name.upper()}") # print(f"\n🟢 正在处理颜色: {color_name.upper()}")
if color_name == 'green':
continue
mask_total = np.zeros(hsv.shape[:2], dtype=np.uint8) mask_total = np.zeros(hsv.shape[:2], dtype=np.uint8)
for (lower, upper) in hsv_ranges: for (lower, upper) in hsv_ranges:
mask = extract_wire_mask(hsv, lower, upper, color_name) mask = extract_wire_mask(hsv, lower, upper, color_name)
Generated
+1076
View File
File diff suppressed because it is too large Load Diff