10.2. 返回一张快照¶
状态端点固然不错,但摄像头存在的理由是它的镜头。添加一个端点,返回传感器此刻正在观察的画面的 JPEG。
import csi
from microdot import Response
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
@app.get('/snapshot.jpg')
async def snapshot(request):
img = csi0.snapshot().compress(quality=85)
return Response(
body=img.bytearray(),
headers={'Content-Type': 'image/jpeg'},
)
从浏览器访问 http://<cam-ip>/snapshot.jpg,当前视图的一张 JPEG 就会填满标签页。刷新一下,你就能得到一张全新的。
10.2.1. Response 对象¶
返回字典的处理函数让 microdot 完成其余工作。JPEG 字节则需要完整形式:一个显式构造的 microdot.Response。body 参数接受任何类字节的值——摄像头的 image.Image 缓冲区通过 bytearray() 暴露出来,因此传感器写入的同一个缓冲区会直接发送到套接字。
Content-Type: image/jpeg 是告诉浏览器把响应体渲染为图像的依据。没有它,浏览器会试图把 JPEG 字节当作文本显示,你看到的将是满屏的乱码。
image.Image.compress() 对现有图像缓冲区就地执行 JPEG 编码,并返回同一张图像(现在已是 JPEG 格式),这样它的字节就能原样发送。quality=85 是常用的默认值——高到足以让画面清晰,又低到足以让文件能通过慢速链路。
10.2.2. 捕获会阻塞循环¶
csi.CSI.snapshot() 会等待传感器完成一帧的曝光和 DMA 传输后才返回。在异步处理函数中,这意味着事件循环会在曝光期间停滞——视光照情况而定,可能是十、二十甚至五十毫秒。当只有一个客户端一次请求一个路由时,这是察觉不到的;但当有多个客户端,或者有一个捕获协程在同时运行时,它就会阻塞所有其他事情。
snapshot() 存在一个非阻塞的变体,用于多协程的情况(blocking=False 会返回下一个就绪的帧,或返回 None)。对于每个请求只取一张快照的情况,默认的阻塞调用就足够了。
现在,所有者可以戳一个 URL 就得到一张全新的帧。