赞
踩
效果
低于20厘米语音提醒字体变红
QQ录屏20240406131651
可直接复制使用(修改自己的WIFI)
- #include <esp32cam.h>
- #include <WebServer.h>
- #include <WiFi.h>
- // 设置要连接的WiFi名称和密码
- const char* WIFI_SSID = "gumou";
- const char* WIFI_PASS = "gu3456789";
- WebServer server(80);
- // 设置不同分辨率的静态变量
- static auto loRes = esp32cam::Resolution::find(320, 240);
- static auto hiRes = esp32cam::Resolution::find(1280, 1024);
- // 处理BMP图像请求
- void handleBmp() {
- if (!esp32cam::Camera.changeResolution(loRes)) {
- Serial.println("SET-LO-RES FAIL");
- }
- auto frame = esp32cam::capture();
- if (frame == nullptr) {
- Serial.println("CAPTURE FAIL");
- server.send(503, "", "");
- return;
- }
-
- if (!frame->toBmp()) {
- Serial.println("CONVERT FAIL");
- server.send(503, "", "");
- return;
- }
-
- server.setContentLength(frame->size());
- server.send(200, "image/bmp");
- WiFiClient client = server.client();
- frame->writeTo(client);
- }
-
- // 服务JPG图像请求
- void serveJpg() {
- auto frame = esp32cam::capture();
- if (frame == nullptr) {
- Serial.println("CAPTURE FAIL");
- server.send(503, "", "");
- return;
- }
-
- server.setContentLength(frame->size());
- server.send(200, "image/jpeg");
- WiFiClient client = server.client();
- frame->writeTo(client);
- }
-
- // 处理低分辨率JPG请求
- void handleJpgLo() {
- if (!esp32cam::Camera.changeResolution(loRes)) {
- Serial.println("SET-LO-RES FAIL");
- }
- serveJpg();
- }
-
- // 处理高分辨率JPG请求
- void handleJpgHi() {
- if (!esp32cam::Camera.changeResolution(hiRes)) {
- Serial.println("SET-HI-RES FAIL");
- }
- serveJpg();
- }
-
- // 处理JPG请求
- void handleJpg() {
- server.sendHeader("Location", "/cam-hi.jpg");
- server.send(302, "", "");
- }
-
- // 处理MJPEG流请求
- void handleMjpeg() {
- if (!esp32cam::Camera.changeResolution(hiRes)) {
- Serial.println("SET-HI-RES FAIL");
- }
-
- Serial.println("STREAM BEGIN");
- WiFiClient client = server.client();
- auto startTime = millis();
- int res = esp32cam::Camera.streamMjpeg(client);
- if (res <= 0) {
- Serial.printf("STREAM ERROR %d\n", res);
- return;
- }
-
- auto duration = millis() - startTime;
- Serial.printf("STREAM END %dfrm %0.2ffps\n", res, 1000.0 * res / duration);
- }
-
- void setup() {
- Serial.begin(115200);
- Serial.println();
-
- // 初始化摄像头
- {
- using namespace esp32cam;
- Config cfg;
- cfg.setPins(pins::AiThinker);
- cfg.setResolution(hiRes);
- cfg.setBufferCount(2);
- cfg.setJpeg(80);
-
- bool ok = Camera.begin(cfg);
- Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
- }
-
- // 连接WiFi
- WiFi.persistent(false);
- WiFi.mode(WIFI_STA);
- WiFi.begin(WIFI_SSID, WIFI_PASS);
- while (WiFi.status() != WL_CONNECTED) {
- delay(500);
- }
-
- // 打印服务器地址和端口
- Serial.print("http://");
- Serial.println(WiFi.localIP());
- Serial.println(" /cam.bmp");
- Serial.println(" /cam-lo.jpg");
- Serial.println(" /cam-hi.jpg");
- Serial.println(" /cam.mjpeg");
-
- // 定义服务器路由
- server.on("/cam.bmp", handleBmp);
- server.on("/cam-lo.jpg", handleJpgLo);
- server.on("/cam-hi.jpg", handleJpgHi);
- server.on("/cam.jpg", handleJpg);
- server.on("/cam.mjpeg", handleMjpeg);
- // 启动服务器
- server.begin();
- }
- void loop() {
- // 处理客户端请求
- server.handleClient();
- }
录入后,在串口监视器处查看IP(自动会输出)
录入前要把波特率调整115200
可更改 1.IP地址 2.提醒语音 3.提醒距离 4.可删除倒数第二行 不显示画面 只测量距离
- import urllib
- import cv2
- import numpy as np
- from cvzone.FaceMeshModule import FaceMeshDetector
- import pygame
- import threading
- from PIL import Image, ImageDraw, ImageFont
- # 初始化pygame.mixer
- pygame.mixer.init()
- # 加载音频文件
- pygame.mixer.music.load('7359.wav') # 靠的太近啦音频
- detector = FaceMeshDetector(maxFaces=1)
- url = 'http://192.168.85.168/cam-hi.jpg' # 改成自己的IP地址+/cam-hi.jpg
- # 定义播放音频的函数
- def play_audio():
- pygame.mixer.music.play(1)
- while pygame.mixer.music.get_busy():
- continue
- # 函数:从ESP32CAM获取图像
- def get_esp32cam_image(url):
- img_resp = urllib.request.urlopen(url)
- img_np = np.array(bytearray(img_resp.read()), dtype=np.uint8)
- img = cv2.imdecode(img_np, -1)
- return img
- # 开始检测人脸距离
- while True:
- # 从ESP32CAM获取图像
- img = get_esp32cam_image(url)
- # 检测人脸
- img, faces = detector.findFaceMesh(img, draw=False)
- if faces:
- face = faces[0]
- point_left = face[145]
- point_right = face[374]
- w, _ = detector.findDistance(point_left, point_right)
- W = 6.3
- f = 600
- d = (W * f) / w
- print(d)
-
- # 设置距离颜色
- if d < 20:
- print("过近提醒")
- # 检查是否正在播放音频
- if not pygame.mixer.music.get_busy():
- # 使用线程播放音频,避免阻塞主程序
- audio_thread = threading.Thread(target=play_audio)
- audio_thread.start()
- text_color = (255, 0, 0) # 红色
- else:
- text_color = (0, 0, 255) # 蓝色
- # 将Depth文本显示为汉语
- pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
- draw = ImageDraw.Draw(pil_img)
- font = ImageFont.truetype("msyh.ttc", 36) # 使用微软雅黑字体,大小为36
- draw.text((face[10][0] - 95, face[10][1] - 5), f'距离:{int(d)}厘米', font=font, fill=text_color)
- img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
- cv2.imshow("Distance recognition", img) #这行注释掉后可以不显示摄像头窗口只输出距离
- if cv2.waitKey(1) == ord('q'):
- break
- cv2.destroyAllWindows()
http://192.168.85.168/cam-hi.jpg
http://192.168.85.168/cam.mjpeg
设计ui之后画面较小 若摄像头分辨率较低建议使用上面的代码
- import sys
- from PyQt5 import QtGui
- from PyQt5.QtWidgets import QApplication, QWidget
- from PyQt5.QtCore import Qt, QPoint, pyqtSignal
- from esp32_ui import Ui_Form # UI
- import cv2
- from cvzone.FaceMeshModule import FaceMeshDetector
- import pygame
- import threading
- from PIL import Image, ImageDraw, ImageFont
- import numpy as np
- import urllib.request
- import ping
- class guWindow(QWidget):
- close_signal = pyqtSignal()
-
- def __init__(self):
- super().__init__()
- self.gu = Ui_Form()
- self.gu.setupUi(self)
- self.gu.lineEdit.returnPressed.connect(self.gumou) # lineEdit回车运行
- self.video_label = self.gu.label_2# 2 QLabel2
- self.user_name_qwidget = self.gu.lineEdit
- self.progress_bar = self.gu.progressBar #进度条
- self.setWindowOpacity(0.90) # 设置窗口透明度
- self.setWindowFlag(Qt.FramelessWindowHint) # 去除边框
- self.setAttribute(Qt.WA_TranslucentBackground) # 去除白色背景
- self.offset = QPoint() # 记录鼠标按下的初始位置
- self.close_signal.connect(self.closeEvent)
-
- def closeEvent(self, event):
- # 关闭窗口时发送信号
- self.stop_capture()
-
- def mousePressEvent(self, event):
- self.offset = event.pos()
-
- def mouseMoveEvent(self, event):
- if event.buttons() == Qt.LeftButton:
- self.move(self.pos() + event.pos() - self.offset) # 移动窗口位置
-
- def gumou(self): # 按钮绑定的函数 功能
- s = self.user_name_qwidget.text()
- self.user_name_qwidget.clear()
- try:
- distance_threshold = float(s) # 将用户输入的文本转换为浮点数作为距离阈值
- except ValueError:
- print("Invalid input. Please enter a valid number.")
- return
-
- pygame.mixer.init()
- # 加载音频文件
- pygame.mixer.music.load('7359.wav') # 靠的太近啦
- self.capture_active = True
-
- def play_audio():
- pygame.mixer.music.play(1)
- while pygame.mixer.music.get_busy():
- continue
-
- while self.capture_active:
- img = get_esp32cam_image('http://192.168.85.168/cam-hi.jpg') # Get image from ESP32CAM
- if img is not None:
- self.detect_and_display(img, distance_threshold, play_audio)
-
- def detect_and_display(self, img, distance_threshold, play_audio):
- self.detector = FaceMeshDetector(maxFaces=1)
- img, faces = self.detector.findFaceMesh(img, draw=False)
- if faces:
- face = faces[0]
- pointLeft = face[145]
- pointRight = face[374]
- w, _ = self.detector.findDistance(pointLeft, pointRight)
- W = 6.5
- f = 600 # 焦距
- d = (W * f) / w
- print(d)
- # 设置距离颜色
- if d < distance_threshold: # 使用用户输入的距离阈值作为判断条件
- print("过近提醒")
- # 检查是否正在播放音频
- if not pygame.mixer.music.get_busy():
- # 使用线程播放音频,避免阻塞主程序
- audio_thread = threading.Thread(target=play_audio)
- audio_thread.start()
- text_color = (255, 0, 0) # 红色
- else:
- text_color = (0, 0, 255) # 蓝色
- # Update the QProgressBar value
- self.progress_bar.setValue(int(d))
- # 将 Depth 文本显示为汉语
- pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
- draw = ImageDraw.Draw(pil_img)
- font = ImageFont.truetype("msyh.ttc", 36) # 使用微软雅黑字体,大小为36
- draw.text((face[10][0] - 95, face[10][1] - 5), f'距离:{int(d)}厘米', font=font, fill=text_color)
- img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
- h, w, c = img.shape
- bytesPerLine = c * w
- if c == 3: # 如果颜色通道为3(BGR)
- q_img = QtGui.QImage(img.data, w, h, bytesPerLine, QtGui.QImage.Format_BGR888)
- else: # 如果颜色通道为4(BGRA)
- q_img = QtGui.QImage(img.data, w, h, bytesPerLine, QtGui.QImage.Format_BGRA8888)
- # Convert QImage to QPixmap
- pixmap = QtGui.QPixmap.fromImage(q_img)
- # Display QPixmap on QLabel
- self.video_label.setPixmap(pixmap)
- self.video_label.setScaledContents(True)
- self.video_label.update()
- cv2.waitKey(1)
-
- def stop_capture(self):
- self.capture_active = False
-
- def get_esp32cam_image(url):
- img_resp = urllib.request.urlopen(url)
- img_np = np.array(bytearray(img_resp.read()), dtype=np.uint8)
- img = cv2.imdecode(img_np, -1)
- return img
-
- if __name__ == '__main__':
- app = QApplication(sys.argv)
- icon = QtGui.QIcon(':/jay.ico')
- app.setWindowIcon(icon)
- # 创建可拖动窗口实例
- ui = guWindow() # 函数
- # 显示窗口
- ui.show()
- # 启动应用程序事件循环
- sys.exit(app.exec_())
从这里下载会自动安装指定位置
不要在图中位置配置,速度太慢!!
由于esp32-cam连WIFI能力较差
(若手机开热点供双方连接,建议esp32-cam先连接后再让电脑连)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。