Mercurial > code > home > repos > homeauto
comparison espNode/readcam.py @ 1740:c77b5ab7b99d
camera work
author | drewp@bigasterisk.com |
---|---|
date | Fri, 01 Sep 2023 17:13:51 -0700 |
parents | bc3516d02762 |
children |
comparison
equal
deleted
inserted
replaced
1739:28a3e35bc23f | 1740:c77b5ab7b99d |
---|---|
1 #!camtest/bin/python3 | 1 #!camtest/bin/python3 |
2 import asyncio | |
2 import binascii | 3 import binascii |
4 import io | |
5 import json | |
3 import logging | 6 import logging |
7 import os | |
4 import time | 8 import time |
5 import io | |
6 import os | |
7 import json | |
8 from docopt import docopt | |
9 from standardservice.logsetup import log, verboseLogging | |
10 | 9 |
11 logging.basicConfig(level=logging.INFO) | 10 import apriltag |
11 import cv2 | |
12 import numpy | |
13 from aioesphomeapi.client import APIClient | |
14 from aioesphomeapi.model import CameraState | |
12 from aiohttp import web | 15 from aiohttp import web |
13 from aiohttp.web import Response | 16 from aiohttp.web import Response |
14 from aiohttp_sse import sse_response | 17 from aiohttp_sse import sse_response |
18 from docopt import docopt | |
15 | 19 |
16 import asyncio | 20 logging.basicConfig(level=logging.INFO) |
21 log = logging.getLogger(__name__) | |
17 | 22 |
18 from aioesphomeapi import APIClient | |
19 from aioesphomeapi.model import CameraState | |
20 import apriltag | |
21 import cv2 | |
22 import numpy | |
23 | 23 |
24 class CameraReceiver: | 24 class CameraReceiver: |
25 def __init__(self, loop, host): | 25 |
26 def __init__(self, host): | |
26 self.lastFrameTime = None | 27 self.lastFrameTime = None |
27 self.loop = loop | |
28 self.host = host | 28 self.host = host |
29 self.lastFrame = b"", '' | 29 self.lastFrame = b"", '' |
30 self.recent = [] | 30 self.recent = [] |
31 | 31 |
32 async def start(self): | 32 async def start(self): |
33 try: | 33 try: |
34 self.c = c = APIClient(self.loop, | 34 self.c = c = APIClient(self.host, 6053, 'MyPassword') |
35 self.host, | |
36 6053, 'MyPassword') | |
37 await c.connect(login=True) | 35 await c.connect(login=True) |
38 await c.subscribe_states(on_state=self.on_state) | 36 await c.subscribe_states(on_state=self.on_state) |
39 except OSError: | 37 except OSError: |
40 loop.stop() | |
41 return | 38 return |
42 self.loop.create_task(self.start_requesting_image_stream_forever()) | 39 self.t = asyncio.create_task( |
40 self.start_requesting_image_stream_forever()) | |
43 | 41 |
44 async def start_requesting_image_stream_forever(self): | 42 async def start_requesting_image_stream_forever(self): |
45 while True: | 43 while True: |
46 try: | 44 try: |
47 await self.c.request_image_stream() | 45 await self.c.request_image_stream() |
48 except AttributeError: | 46 except AttributeError: |
49 self.loop.stop() | |
50 return | 47 return |
51 # https://github.com/esphome/esphome/blob/dev/esphome/components/esp32_camera/esp32_camera.cpp#L265 says a 'stream' is 5 sec long | 48 # https://github.com/esphome/esphome/blob/dev/esphome/components/esp32_camera/esp32_camera.cpp#L265 says a 'stream' is 5 sec long |
52 await asyncio.sleep(4) | 49 await asyncio.sleep(4) |
53 | 50 |
54 def on_state(self, s): | 51 def on_state(self, s): |
55 if isinstance(s, CameraState): | 52 if isinstance(s, CameraState): |
56 jpg = s.image | 53 jpg = s.data |
57 if len(self.recent) > 10: | 54 if len(self.recent) > 10: |
58 self.recent = self.recent[-10:] | 55 self.recent = self.recent[-10:] |
59 | 56 |
60 self.recent.append(jpg) | 57 self.recent.append(jpg) |
61 #print('recent lens: %s' % (','.join(str(len(x)) | 58 #print('recent lens: %s' % (','.join(str(len(x)) |
62 # for x in self.recent))) | 59 # for x in self.recent))) |
63 else: | 60 else: |
64 print('other on_state', s) | 61 print('other on_state', s) |
65 | 62 |
66 def analyze(self, jpg): | 63 def analyze(self, jpg): |
67 img = cv2.imdecode(numpy.asarray(bytearray(jpg)), | 64 img = cv2.imdecode(numpy.asarray(bytearray(jpg)), cv2.IMREAD_GRAYSCALE) |
68 cv2.IMREAD_GRAYSCALE) | |
69 result = detector.detect(img) | 65 result = detector.detect(img) |
70 msg = {} | 66 msg = {} |
71 if result: | 67 if result: |
72 center = result[0].center | 68 center = result[0].center |
73 msg['center'] = [round(center[0], 2), round(center[1], 2)] | 69 msg['center'] = [round(center[0], 2), round(center[1], 2)] |
90 | 86 |
91 | 87 |
92 def imageUri(jpg): | 88 def imageUri(jpg): |
93 return 'data:image/jpeg;base64,' + binascii.b2a_base64(jpg).decode('ascii') | 89 return 'data:image/jpeg;base64,' + binascii.b2a_base64(jpg).decode('ascii') |
94 | 90 |
91 | |
95 async def stream(request): | 92 async def stream(request): |
96 async with sse_response(request) as resp: | 93 async with sse_response(request) as resp: |
97 await resp.send(imageUri(recv.lastFrame[0])) | 94 await resp.send(imageUri(recv.lastFrame[0])) |
98 await resp.send(json.dumps(recv.lastFrame[1])) | 95 await resp.send(json.dumps(recv.lastFrame[1])) |
99 async for frame, msg in recv.frames(): | 96 async for frame, msg in recv.frames(): |
100 await resp.send(json.dumps(msg)) | 97 await resp.send(json.dumps(msg)) |
101 await resp.send(imageUri(frame)) | 98 await resp.send(imageUri(frame)) |
102 return resp | 99 return resp |
100 | |
103 | 101 |
104 async def index(request): | 102 async def index(request): |
105 d = r""" | 103 d = r""" |
106 <html> | 104 <html> |
107 <body> | 105 <body> |
144 | 142 |
145 arguments = docopt(''' | 143 arguments = docopt(''' |
146 this | 144 this |
147 | 145 |
148 Usage: | 146 Usage: |
149 this [-v] [--cam host] [--port to_serve] | 147 this [-v] [--cam host] [--port to_serve] |
150 | 148 |
151 Options: | 149 Options: |
152 -v --verbose more log | 150 --port n http server [default: 10020] |
153 --port n http server [default: 10020] | 151 --cam host hostname of esphome server |
154 --cam host hostname of esphome server | |
155 ''') | 152 ''') |
156 | 153 |
157 verboseLogging(arguments['--verbose']) | |
158 logging.getLogger('aioesphomeapi.connection').setLevel(logging.INFO) | 154 logging.getLogger('aioesphomeapi.connection').setLevel(logging.INFO) |
159 | 155 |
160 loop = asyncio.get_event_loop() | 156 recv = CameraReceiver(arguments['--cam']) |
161 | 157 |
162 recv = CameraReceiver(loop, arguments['--cam']) | |
163 detector = apriltag.Detector() | 158 detector = apriltag.Detector() |
164 | 159 |
165 f = recv.start() | 160 f = recv.start() |
166 loop.create_task(f) | 161 async def starter(app): |
162 asyncio.create_task(f) | |
167 | 163 |
168 start_time = time.time() | 164 start_time = time.time() |
169 app = web.Application() | 165 app = web.Application() |
166 app.on_startup.append(starter) | |
170 app.router.add_route('GET', '/stream', stream) | 167 app.router.add_route('GET', '/stream', stream) |
171 app.router.add_route('GET', '/', index) | 168 app.router.add_route('GET', '/', index) |
172 try: | 169 web.run_app(app, port=int(arguments['--port'])) |
173 web.run_app(app, host='0.0.0.0', port=int(arguments['--port'])) | |
174 except RuntimeError as e: | |
175 log.error(e) | |
176 log.info(f'run_app stopped after {time.time() - start_time} sec') |