Raspberry Pi: веб-камера и OLED-дисплей

Монохромные дисплеи обычно используются для отображения какой-нибудь примитивной графики: текста, графиков, диаграмм и пр. Если вывести на тот же OLED-дисплей с разрешением 128×64 пикселя фотографию, картинка будет не ахти какой, но всё же вполне интерпретируемой.

А что, если вообще транслировать на такой индикатор потоковое изображение с камеры? В этой статье проведем именно такой эксперимент. Практической пользы он не несет, но выглядеть такое устройство будет весьма необычно.

Список компонентов

Для эксперимента нам понадобится Raspberry Pi 4, разумеется, с блоком питания и SD-картой памяти. Модуль OLED-дисплея и провода.

В корзину
В корзину
В корзину
В корзину
В корзину

Подготовка

Прежде чем разбираться с трансляцией, подключим OLED дисплей к микрокомпьютеру по схеме из одноимённого урока Raspberry Pi: работа с OLED дисплеем. Там же смотрим какие библиотеки нужно установить для работы дисплея.

Для организации потока используем подход, который часто применяется для трансляции видео с веб-камеры в интернет. Подход заключается в том, чтобы постоянно записывать изображение с веб-камеры во временный файл, который затем используется для просмотра в браузере. Мы же будем считывать содержимое этого файла и выводить его на OLED-дисплей.

И начнём мы с того, что должным образом подготовим место для сохраняемого изображения. Можно этот шаг пропустить, но важно помнить, что при хранении этого файла на SD-карте мы получим сразу две проблемы:

  • при постоянной перезаписи файла (даже 1 раз в секунду) мы будем тратить ресурс SD-карты;
  • скорость записи/чтения на флеш-память не такая высокая, как в случае памяти ОЗУ.

Собственно, решение очевидно — будем хранить файл в оперативной памяти. Обозначим в настройках накопителей папку tmp, как хранимую в ОЗУ. Для этого откроем в редакторе файл настроек:

sudo nano /etc/fstab

и добавим в него строку:

tmpfs /tmp tmpfs defaults,noatime,nosuid 0 0

Должно получиться так:

Сохраняем файл Ctrl+O, выходим Ctrl+X и перезагружаем Raspberry Pi.

Утилита для чтения веб-камеры

Чем считывать картинку с веб-камеры? На самом деле решений несколько:

  • утилита fswebcam;
  • утилита mjpg-stream;
  • пакет motion;
  • opencv;
  • для камеры с CSI интерфейсом подойдет raspistill.

Используем, на мой взгляд, самую простую fswebcam. Устанавливаем её через консоль:

sudo apt-get install fswebcam

Проверим работу утилиты командой:

fswebcam /tmp/pic.jpg

После завершения работы утилиты, в папке tmp появится изображение с камеры. Следующий шаг — сохранение изображение в этот файл автоматически, каждую секунду.

Для удобства, а может и для будущей демонизации, создаём shell-скрипт для запуска fswebcam с нужными параметрами:

cd /home/pi
touch webcam.sh

открываем созданный файл в редакторе nano:

nano webcam.sh

и пишем такой код:

#!/bin/bash

fswebcam -q -r 160x120 --loop 1 --no-banner --exec 'mv /tmp/stream/pic.jpg /tmp/last.jpg' /tmp/pic.jpg

Сохраняем Ctrl+O, выходим Ctrl+X.

Поясню параметры.

  • q — не отображать служебную информацию в процессе;
  • r — разрешение изображения; у OLED-дисплея разрешение 128×64, так что можно поставить ближайшее, поддерживаемое камерой;
  • loop 1 — считывать изображение каждую секунду;
  • exec ‘mv /tmp/stream/pic.jpg /tmp/last.jpg’ — перед записью нового файла, прежний копировать в файл last.jpg; это поможет избавиться от попыток OLED-скрипта открыть файл, в который fswebcam еще пишет данные;
  • последний параметр — целевой файл.

Теперь сделаем shell-скрипт исполняемым:

chmod +x webcam.sh

И запустим:

webcam.sh

Проверяем папку tmp. Теперь там есть два файла: last.jpg и pic.jpg, которые постоянно обновляются.

Python-скрипт для вывода на OLED-дисплей

Чтобы не нарушать процесс записи изображения с камеры в файл, откроем новую консоль. Создадим файл с расширением .py в домашней папке и откроем его в редакторе:

touch oledcam.py
nano oledcam.py

Напишем программу, которая будет в бесконечном цикле считывать изображение из файла /tmp/pic.jpg и выводить его на OLED.

import board
import adafruit_ssd1306
import time
from PIL import Image

width = 128
height = 64

# инициализация дисплея
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(width, height, i2c, addr=0x3D)

time.sleep(1)

while True:
    oled.fill(0) # чистим экран
    image = Image.open('/tmp/stream/last.jpg') # открываем файл
    image_r = image.resize((width,height), Image.BICUBIC) # масштабируем изображение в размер дисплея
    image_bw = image_r.convert("1") # преобразуем в монохромную матрицу точек
    for x in range(width):
        for y in range(height):
             oled.pixel(x,y,bool(int(image_bw.getpixel((x,y))))) # точка за точкой выводим в буфер дисплея
    oled.show() # выводим изображение на дисплей
    time.sleep(1) # пауза между кадрам 1 секунда

Сохраняем файл и запускаем его:

python3 oledcam.py

Готово. Теперь у нас параллельно запущены две программы: одна сохраняет кадры в файл, а вторая считывает их и выводит на дисплей.

Raspberry Pi. Oled, Python, веб-камера

Дальнейшие размышления

К сожалению, мне не удалось добиться от fswebcam скорости большей, чем 1 кадр в 2-3 секунды. Если у кого то получится, пишите в комментариях.

Если вы используете родную камеру с CSI интерфейсом, можно попробовать сохранять изображения с помощью утилиты raspistill. Строка запуска утилиты может быть такой:

raspistill --nopreview -w 160 -h 120 -q 75 -o /tmp/pic.jpg -tl 500 -t 9999999

Здесь параметр tl — задает период получения снимка в миллисекундах, t — общее время работы скрипта, q — степень сжатия jpeg.


Изменено:

Raspberry Pi: веб-камера и OLED-дисплей: 2 комментария

  1. Строку:
    oled.fill(0) # чистим экран
    оставьте перед WHILE, чтобы очистка происходила только при первом запуске программы, а не при каждом цикле прорисовки изображения.
    Тогда удастся выводить больше кадров (если убрать строку time.sleep(1))

    • Скрипт по отрисовке кадра достаточно быстрый, даже с учетом очистки (хотя fill лишнее). Пауза 1сек стоит уже исходя из скорости формирования файлов, они всё равно быстрее не генерируются. То есть узкое место, всё-таки, утилита fswebcam.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.