annotate espNode/readcam.py @ 773:bc3516d02762

old changes in espNode Ignore-this: cbdcaf859b465e76c2c98e0d4510a1d3
author drewp@bigasterisk.com
date Sun, 28 Jun 2020 14:05:12 -0700
parents b14020802f8a
children c77b5ab7b99d
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
1 #!camtest/bin/python3
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
2 import binascii
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
3 import logging
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
4 import time
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
5 import io
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
6 import os
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
7 import json
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
8 from docopt import docopt
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
9 from standardservice.logsetup import log, verboseLogging
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
10
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
11 logging.basicConfig(level=logging.INFO)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
12 from aiohttp import web
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
13 from aiohttp.web import Response
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
14 from aiohttp_sse import sse_response
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
15
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
16 import asyncio
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
17
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
18 from aioesphomeapi import APIClient
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
19 from aioesphomeapi.model import CameraState
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
20 import apriltag
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
21 import cv2
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
22 import numpy
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
23
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
24 class CameraReceiver:
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
25 def __init__(self, loop, host):
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
26 self.lastFrameTime = None
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
27 self.loop = loop
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
28 self.host = host
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
29 self.lastFrame = b"", ''
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
30 self.recent = []
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
31
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
32 async def start(self):
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
33 try:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
34 self.c = c = APIClient(self.loop,
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
35 self.host,
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
36 6053, 'MyPassword')
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
37 await c.connect(login=True)
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
38 await c.subscribe_states(on_state=self.on_state)
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
39 except OSError:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
40 loop.stop()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
41 return
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
42 self.loop.create_task(self.start_requesting_image_stream_forever())
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
43
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
44 async def start_requesting_image_stream_forever(self):
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
45 while True:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
46 try:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
47 await self.c.request_image_stream()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
48 except AttributeError:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
49 self.loop.stop()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
50 return
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
51 # https://github.com/esphome/esphome/blob/dev/esphome/components/esp32_camera/esp32_camera.cpp#L265 says a 'stream' is 5 sec long
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
52 await asyncio.sleep(4)
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
53
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
54 def on_state(self, s):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
55 if isinstance(s, CameraState):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
56 jpg = s.image
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
57 if len(self.recent) > 10:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
58 self.recent = self.recent[-10:]
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
59
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
60 self.recent.append(jpg)
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
61 #print('recent lens: %s' % (','.join(str(len(x))
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
62 # for x in self.recent)))
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
63 else:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
64 print('other on_state', s)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
65
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
66 def analyze(self, jpg):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
67 img = cv2.imdecode(numpy.asarray(bytearray(jpg)),
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
68 cv2.IMREAD_GRAYSCALE)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
69 result = detector.detect(img)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
70 msg = {}
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
71 if result:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
72 center = result[0].center
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
73 msg['center'] = [round(center[0], 2), round(center[1], 2)]
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
74 return msg
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
75
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
76 async def frames(self):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
77 while True:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
78 if self.recent:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
79 if self.lastFrameTime and time.time() - self.lastFrameTime > 15:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
80 print('no recent frames')
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
81 os.abort()
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
82
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
83 jpg = self.recent.pop(0)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
84 msg = self.analyze(jpg)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
85 yield jpg, msg
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
86 self.lastFrame = jpg, msg
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
87 self.lastFrameTime = time.time()
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
88 else:
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
89 await asyncio.sleep(.05)
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
90
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
91
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
92 def imageUri(jpg):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
93 return 'data:image/jpeg;base64,' + binascii.b2a_base64(jpg).decode('ascii')
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
94
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
95 async def stream(request):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
96 async with sse_response(request) as resp:
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
97 await resp.send(imageUri(recv.lastFrame[0]))
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
98 await resp.send(json.dumps(recv.lastFrame[1]))
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
99 async for frame, msg in recv.frames():
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
100 await resp.send(json.dumps(msg))
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
101 await resp.send(imageUri(frame))
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
102 return resp
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
103
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
104 async def index(request):
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
105 d = r"""
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
106 <html>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
107 <body>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
108 <style>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
109 #center {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
110 position: absolute;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
111 font-size: 35px;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
112 color: orange;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
113 text-shadow: black 0 1px 1px;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
114 margin-left: -14px;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
115 margin-top: -23px;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
116 }
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
117 </style>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
118 <script>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
119 var evtSource = new EventSource("/stream");
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
120 evtSource.onmessage = function(e) {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
121 if (e.data[0] == '{') {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
122 const msg = JSON.parse(e.data);
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
123 const st = document.querySelector('#center').style;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
124 if (msg.center) {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
125 st.left = msg.center[0];
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
126 st.top = msg.center[1];
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
127 } else {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
128 st.left = -999;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
129 }
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
130 } else {
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
131 document.getElementById('response').src = e.data;
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
132 }
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
133 }
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
134 </script>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
135 <h1>Response from server:</h1>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
136 <div style="position: relative">
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
137 <img id="response"></img>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
138 <span id="center" style="position: absolute">&#x25ce;</span>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
139 </div>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
140 </body>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
141 </html>
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
142 """
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
143 return Response(text=d, content_type='text/html')
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
144
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
145 arguments = docopt('''
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
146 this
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
147
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
148 Usage:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
149 this [-v] [--cam host] [--port to_serve]
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
150
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
151 Options:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
152 -v --verbose more log
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
153 --port n http server [default: 10020]
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
154 --cam host hostname of esphome server
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
155 ''')
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
156
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
157 verboseLogging(arguments['--verbose'])
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
158 logging.getLogger('aioesphomeapi.connection').setLevel(logging.INFO)
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
159
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
160 loop = asyncio.get_event_loop()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
161
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
162 recv = CameraReceiver(loop, arguments['--cam'])
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
163 detector = apriltag.Detector()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
164
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
165 f = recv.start()
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
166 loop.create_task(f)
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
167
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
168 start_time = time.time()
685
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
169 app = web.Application()
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
170 app.router.add_route('GET', '/stream', stream)
7c5953801cf1 hall cam
drewp@bigasterisk.com
parents:
diff changeset
171 app.router.add_route('GET', '/', index)
773
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
172 try:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
173 web.run_app(app, host='0.0.0.0', port=int(arguments['--port']))
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
174 except RuntimeError as e:
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
175 log.error(e)
bc3516d02762 old changes in espNode
drewp@bigasterisk.com
parents: 726
diff changeset
176 log.info(f'run_app stopped after {time.time() - start_time} sec')