esp32cam+openCV图像分析传统家电LED面板数据反馈bliner APP-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1400|回复: 8

[分享] esp32cam+openCV图像分析传统家电LED面板数据反馈bliner APP

[复制链接]
发表于 2022-4-27 17:29 | 显示全部楼层 |阅读模式
本帖最后由 yuezhiying 于 2022-4-27 17:56 编辑

      blinker改造一些传统家电时,比如热水器、空调等代有LED数字面板的设备时,用esp32或者8266进行远程红外遥控非常容,但无法得知遥控结果,不能得到返回数据。
     此方案通过用blinker APP 控制ESP32CAM对LED面板拍照后,将图片通过TCP发送至python架设的openCV图像分析程序的服务器,对图像进行分析,得到面板数据上的数据后,返回给ESP32CAM,ESP32CAM得到数据后在反馈给blinker APP,从而实现传统家电的LED面板图像信息数字化的转换。

       python openCV程序 就是开启了一个TCP server端等待图像接收,接受到图像后,通过openCV的模板匹配功能进行数字分析,因为这个应用场景时固定的,内容就上0-9的数字,所以不需要复杂的图形处理,只需要先把0-9的数字都拍照一遍制作成模板,在收到图像后对固定位置数字的图形区域进行裁剪,然后就行轮询匹配找到最相似的图形的索引号便得到数字结果。


python程序为3.1版本,需要安装openCV库。该代码模板匹配并通用,需要自行先将被识别的数字0-9先采集一遍保存至程序目录下的/num目录中作为模板,被识别的数字位置需要根据实际情况裁剪位置坐标。

  1. import cv2
  2. import numpy as np
  3. from imutils import contours
  4. import socket
  5. import time

  6. MaxBytes=1024*3024

  7. def load_digits():
  8. # 加载数字模板
  9.     dig= []
  10.     for q in range(0,10):
  11.         img = cv2.imread("./num/num"+str(q)+".jpg") # 读取图片
  12.         img = img[400:600,400:520]
  13.         img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度处理
  14.          #自动阈值二值化 把图片处理成黑底白字
  15.         img_temp = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU)[1]

  16.         kernel = np.ones((10, 10), np.uint8)
  17.         img_temp = cv2.dilate(img_temp, kernel, iterations = 1)

  18.         # cv2.imshow('num'+str(q), img_temp)
  19.         dig.append(img_temp)  #数字模板存入dig

  20.     return dig

  21. def cont_digits(img):
  22. #模板匹配数字
  23.     img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  24.     img_temp = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU)[1]

  25.     kernel = np.ones((10, 10), np.uint8)
  26.     img_temp = cv2.dilate(img_temp, kernel, iterations = 1)

  27.     img_t = []
  28.     img_t.append(img_temp[400:600,280:400])  #裁剪第一个数字
  29.     img_t.append(img_temp[400:600,400:520])  #裁剪第二个数字

  30.     cv2.imshow("img_t0",img_t[0])
  31.     cv2.imshow("img_t1",img_t[1])

  32.     num_out = []
  33.     for img_T in img_t:     #对每个数字循环匹配十个数字模板
  34.         source = []
  35.         for dig_T in dig:
  36.             res = cv2.matchTemplate(img_T, dig_T, cv2.TM_CCOEFF_NORMED)

  37.             max_val = cv2.minMaxLoc(res)[1]
  38.             #print(max_val)
  39.             source.append(max_val)   #每个模板匹配值存入source
  40.         num_out.append(str(source.index(max(source)))) #取source最大值的索引号为匹配到的数字
  41.         
  42.     return num_out
  43.     #print(num_out)

  44. def bytes2cv(im):
  45.     '''二进制图片转cv2
  46.     :param im: 二进制图片数据,bytes
  47.     :return: cv2图像,numpy.ndarray
  48.     '''
  49.     return cv2.imdecode(np.array(bytearray(im), dtype='uint8'), cv2.IMREAD_UNCHANGED)  # 从二进制图片数据中读取

  50. def cv2bytes(im):
  51.     '''cv2转二进制图片
  52.     :param im: cv2图像,numpy.ndarray
  53.     :return: 二进制图片数据,bytes
  54.     '''
  55.     return np.array(cv2.imencode('.png', im)[1]).tobytes()

  56. server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  57. server.settimeout(None)
  58. host = '192.168.2.162'
  59. #host = socket.gethostname()
  60. port = 8889
  61. server.bind((host, port))        # 绑定端口
  62. j = 0
  63. server.listen(1)                      # 监听

  64. dig = load_digits()

  65. while True:
  66.     try:
  67.         client,addr = server.accept()          # 等待客户端连接
  68.         imgdata = []
  69.         print(addr," 连接上了")
  70.         while True:
  71.             data = client.recv(MaxBytes)
  72.             if not data:
  73.                 print('接收结束')
  74.                 break
  75.             localTime = time.asctime( time.localtime(time.time()))
  76.             print(localTime,' 接收到数据字节数:',len(data))
  77.             if len(data) == 1 and data == b' ':
  78.                 print('接收结束')
  79.                 break
  80.             imgdata.append(data)
  81.         a = bytes()

  82.         for i in imgdata:
  83.             a = a + i

  84.         img = bytes2cv(a)  #接收到的二进制图像数据转换成openCV图像
  85.         num = cont_digits(img)  #进行图像分析
  86.         
  87.         print(num)  #打印分析结果
  88.         out_num = num[0]+num[1]

  89.         client.send(out_num.encode('utf-8'))  #向客户端返回分析结果
  90.                

  91.         cv2.imshow("fcrame",img)
  92.         key = cv2.waitKey(1) & 0xFF
  93.         # 按'q'健退出循环
  94.         if key == ord('q') or cv2.waitKey(1)&0xff == 27:
  95.             break

  96.         
  97.     except BaseException as e:
  98.         print("出现异常:")
  99.         print(repr(e))
复制代码



 楼主| 发表于 2022-4-27 17:48 | 显示全部楼层
实测效果
微信图片_20220427094129.jpg
微信图片_20220427094041.png
微信图片_20220427094147.jpg
该方案虽然有点绕弯子,但是适用方向比较广,只要可以拍照的面板都可做识别,无需修改硬件获取反馈数据。
图像识别服务器可以搭建在公网,给不同得设备公用,或者搭建在家里的nas、树莓派、香橙派等都可。

 楼主| 发表于 2022-4-27 17:36 | 显示全部楼层
Arduino 代码中使用了esp32cam.h库在附件中下载,blinker程序只是测试返回数据,没有写其他功能。

  1. #define BLINKER_WIFI

  2. #include <Blinker.h>
  3. #include <esp32cam.h>


  4. static auto hiRes = esp32cam::Resolution::find(800, 600);

  5. char auth[] = "*****";
  6. char ssid[] = "******";
  7. char pswd[] = "******";

  8. const uint16_t port = 8889;  //  python openCV服务器端口
  9. const char * host = "192.168.2.162"; //  python openCV服务器地址

  10. // 新建组件对象
  11. BlinkerButton Button1("btn-up");
  12. BlinkerButton Button2("btn-down");
  13. BlinkerNumber Number1("num");

  14. int counter = 0;

  15. // 按下按键即会执行该函数
  16. void button1_callback(const String & state)
  17. {
  18.     BLINKER_LOG("get button state: ", state);
  19.     serveJpg();
  20. }


  21. void button2_callback(const String & state)
  22. {
  23.     BLINKER_LOG("get button state: ", state);
  24.     serveJpg();
  25. }

  26. // 如果未绑定的组件被触发,则会执行其中内容
  27. void dataRead(const String & data)
  28. {
  29.     BLINKER_LOG("Blinker readString: ", data);
  30. }


  31. void serveJpg()
  32. {
  33.   auto frame = esp32cam::capture();
  34.   if (frame == nullptr) {
  35.     Serial.println("CAPTURE FAIL");
  36.     return;
  37.   }
  38.   Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
  39.                 static_cast<int>(frame->size()));
  40.                

  41.   WiFiClient client;
  42.   client.connect(host, port);
  43.   frame->writeTo(client);
  44.   delay(100);
  45.   client.write(" ");
  46.   while (client.connected() || client.available()){
  47.     if (client.available()){
  48.       String line = client.readString();
  49.       Serial.println(line);
  50.       Number1.print(line.toInt());
  51.       client.stop();
  52.     }
  53.   }
  54. }

  55. void setup()
  56. {
  57.     // 初始化串口
  58.     Serial.begin(115200);
  59.     BLINKER_DEBUG.stream(Serial);
  60.     //BLINKER_DEBUG.debugAll();

  61.     // 初始化blinker
  62.     Blinker.begin(auth, ssid, pswd);
  63.     Blinker.attachData(dataRead);

  64.     Button1.attach(button1_callback);
  65.     Button2.attach(button2_callback);
  66.   {
  67.     using namespace esp32cam;
  68.     Config cfg;
  69.     cfg.setPins(pins::AiThinker);
  70.     cfg.setResolution(hiRes);
  71.     cfg.setBufferCount(2);
  72.     cfg.setJpeg(80);

  73.     bool ok = Camera.begin(cfg);
  74.     Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
  75.   }
  76.    
  77. }

  78. void loop() {
  79.     Blinker.run();
  80. }
复制代码


 楼主| 发表于 2022-4-27 17:51 | 显示全部楼层
该项目未使用系统自带esp32cam库,使用附件中的库。

esp32cam-main.zip

16.2 KB, 下载次数: 16

esp32cam库

发表于 2022-4-27 18:54 | 显示全部楼层
希望不是搬的,才刚刷到视频,支持ZR
发表于 2022-4-27 21:33 | 显示全部楼层
刚在抖音看了,很棒。
其实我们也在服务器上装了OCR和人脸识别,但对服务器压力比较大,还在研究如何提供服务。
我觉得自己部署也是个很好的方式,我们会考虑下提供一个自己部署识别的方案。
 楼主| 发表于 2022-4-28 11:08 | 显示全部楼层
点灯官方 发表于 2022-4-27 21:33
刚在抖音看了,很棒。
其实我们也在服务器上装了OCR和人脸识别,但对服务器压力比较大,还在研究如何提供服 ...

那可太好了,希望点灯科技越做越好!
发表于 2022-4-29 18:21 | 显示全部楼层
15行的 img[ 200:540,120:670] 这个img的参数怎么解读?模板有什么要求吗,能有白边吗?我运行起来的识别正确率很低.
发表于 2022-5-23 22:25 | 显示全部楼层
太棒了,我也真想用这种方法来改造一下我的热水器。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 09:27 , Processed in 0.109929 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表