01 | На заметку: |
У данной статьи есть видеоверсия!
Подписывайтесь на канал
, чтобы быть в курсе обновлений! |
|
03 |
Введение Существует всего 2 вида камер, предлагаемых официальным сообществом Raspberry — обычная камера и камера ночного видения (инфракрасная камера для съемки в условиях слабого освещения): |
|
05 |
Это второе поколение модулей с камерами для Raspberry Pi. От первого поколения они отличаются новой 8-мегапиксельной матрицей Sony IMX219 с разрешением 3280×2464 пикселей (подобные матрицы устанавливаются в некоторые мобильные телефоны), вместо 5-мегапиксельной OmniVision OV5647 с разрешением 2592×1944 пикселей. Замена матрицы не принесла увеличения производительности. Режимы записи видео остались прежними: 1080p30, 720p60 и 640×480p60/90. Тем не менее, был сделал серьезный скачок в качестве изображения. |
|
07 |
В данной статье будет использован китайский клон первой версии оригинальной камеры, так как он в 4 раза дешевле оригинала: |
|
09 |
Все описанное в данной статье также справедливо и для оригинальных камер. |
|
10 | На заметку: |
Перед всеми манипуляциями, как обычно, необходимо обновить систему:
1 2 sudo apt-get update
sudo apt-get dist-upgrade |
|
11 |
Подключение и настройка Камера общается с Малинкой по интерфейсу CSI, и подключается к специальному разъему на плате: |
|
13 | На заметку: |
Интерфейс CSI, в отличие от USB-камер, позволяет не загружать процессор Малинки и использовать камеру максимально эффективно.
|
|
14 | На заметку: |
Камера потребляет ток 200-250мА — это необходимо учитывать, если используется слабый источник питания.
|
|
15 |
Подключив камеру к Малинке, в настройках Raspberry OS (Preferences → Raspberry Pi Configuration) необходимо включить возможность использования камеры — вкладка Interfaces пункт Camera кликнуть на Enabled. |
|
16 |
|
Для более удобной работы с камерой может пригодиться пластиковый кронштейн: |
18 |
После перезагрузки, камера готова к использованию. |
|
19 | На заметку: |
В случае возникновения проблем с эксплуатацией камеры, в первую очередь, необходимо методом простой прозвонки, удостовериться в наличии контакта между Малинкой и камерой.
Также нужно убедиться, что разъем, соединяющий камеру с платой, тоже надежно зафиксирован. |
|
20 |
Базовый функционал Для базовых манипуляций с камерой существует 3 утилиты командной строки, которые предустановлены в системе:
|
|
21 |
Также эти утилиты позволяют осуществлять Timelapse-съемку — это такой вид съемки, при котором создается набор фотографий сделанных с заданным интервалом, который потом сшивается в одно видео, создавая эффект ускорения. |
|
22 |
Все утилиты должны запускаться с параметрами. |
|
23 | На заметку: |
Подробно о параметрах стандартных утилит по работе с камерой можно почитать на официальном ресурсе Raspberry Py Camera Module.
Полный список параметров для каждой утилиты можно получить, если выполнить утилиту без параметров 1 raspistill либо выполнить утилиту с параметром --help: 1 raspistill --help |
|
24 |
Ряд параметров является общим для всех этих утилит и разбит на 2 группы:
|
|
25 |
Остальные параметры специфичны для каждой утилиты. |
|
26 | На заметку: |
Необязательный параметр -t устанавливает время работы утилиты в миллисекундах. В случае работы утилиты raspistill (raspiyuv), фотография будет сделана по завершении этого времени. Для утилиты raspivid этот параметр задает длительность съемки видео.
Если параметр -t опущен, по умолчанию, используется время работы в 5 секунд. |
|
27 | На заметку: |
Если, горящий во время работы камеры, светодиод мешает (например, отражается в объектив во время ночной съемки через стекло), его можно отключить. Для этого в файл config.txt:
1 sudo nano /boot/config.txt Нужно добавить строку: 1 disable_camera_led=1 После — сохранить файл config.txt и перезагрузить Малинку |
|
28 |
Примеры использования стандартных утилит Далее приведены примеры использования утилит: |
|
29 |
1 raspistill -t 2000 -o image.jpg -w 640 -h 480 -v |
|
30 |
Сделать фото с отсрочкой в 2 секунды (-t 2000), разрешением 640×480 (-w 640 -h 480) с выводом информации во время работы утилиты (-v) и сохранить в файл image.jpg (-o image.jpg).
|
|
31 |
1 raspivid -t 10000 -o video.h264 |
|
32 |
Записать видео длиной 10 секунд (-t 10000) и сохранить в файл video.h264 (-o video.h264).
|
|
33 | На заметку: |
Для отражения изображения по горизонтали/вертикали нужно использовать параметры -hf и -vf.
|
|
34 |
Для воспроизведения видео можно воспользоваться предустановленным плеером OMXPlayer. Если по каким-то причинам его нет, установить его можно командой: |
|
35 |
1 sudo apt-get install omxplayer |
|
36 |
OMXPlayer — плеер командной строки, без визуального интерфейса. Чтобы запустить ролик, нужно вызвать плеер с указанием, в качестве параметра, имени воспроизводимого файла: |
|
37 |
1 omxplayer video.h264 |
|
38 |
Поскольку файлы с разрешением .h264, записанные утилитой raspivid, по сути являются сжатым видеопотоком без медиаконтейнера, то при попытке воспроизвести их, при помощи OMXPlayer, они будут воспроизводиться с увеличенной скоростью, вне зависимости от заданной при записи частоты кадров. Для того, чтобы сделать записанные файлы нормально воспроизводимыми, их нужно поместить в любой медиаконтейнер — mkv, avi, mp4 и т.д. Делается это при помощи утилиты avconv — параметром -r задается частота кадров нового видео: |
Более подробно речь об утилите avconv пойдет далее в статье.
|
39 |
1 avconv -i video.h264 -r 30 -vcodec copy video.mkv |
|
40 | На заметку: |
После того, как видео помещено в видеоконтейнер, воспроизвести его можно будет только при помощи медиаплеера KODI (либо на любом настольном компьютере, в т.ч. и на Windows), так как он умеет использовать аппаратное декодирование видео. Ни OMXPlayer, ни VLCPlayer не смогут корректно воспроизвести полученный файл на Raspberry Pi 3.
|
|
41 |
Timelapse-видео Процесс создания Timelapse-видео состоит из 2 этапов. На первом этапе создается набор фотографий, сделанных с заданным интервалом при помощи утилиты raspistill с параметрами -o, -t и -tl:
|
|
42 |
Пример: |
|
43 |
1 raspistill -o myphoto%04d.jpg -t 30000 -tl 2000 |
|
44 |
Сделать серию фото в течение 30 секунд (-t 30000), с паузой между фотографиями в 2 секунды (-tl 2000) и сохранить фотографии с именами myphoto0000.jpg, myphoto0001.jpg, myphoto0002.jpg и т.д. (-o myphoto%04d.jpg).
|
|
45 | На заметку: |
В случае появления сообщения о пропуске кадров Skipping frame X to restart at frame Y:
Сообщение говорит о том, что камера не успевает сделать снимок и он будет пропущен, необходимо:
|
|
46 |
После этого все полученные снимки нужно собрать в одно видео при помощи утилиты avconv. |
|
47 |
Если утилита не установлена в системе, установить её можно командой: |
|
48 |
1 sudo apt-get install libav-tools |
|
49 |
Утилита avconv требовательна к нумерации файлов. |
|
50 |
В случае, если необходимо собрать видео из файлов, нумерация которых начинается не с 0000, необходимо привести имена файлов к виду, подходящему для обработки утилитой avconv |
|
51 |
Для управления процессом сборки могут понадобиться некоторые параметры:
|
Подробно об утилите можно почитать на официальной странице.
|
52 |
1 avconv -start_number 213 -i image_%5d.jpg -r 30 -vcodec libx264 -q:v 3 output.mp4 |
|
53 | На заметку: |
На самом деле возможности утилиты avconv намного шире. Например, с её помощью можно осуществлять видеозахват области со слежением за мышью с экрана вашей Малинки:
1 avconv -f x11grab -follow_mouse centered -show_region 1 -r 25 -t 10 -s cif -i :0.0 video.mkv Ну а для полноценного захвата рабочего стола Малинка слабовата: 1 avconv -f x11grab -r 25 -s 1920x1080 -t 10 -i :0.0 -vcodec libx264 video.mkv |
|
54 |
Использование камеры из Python — библиотека PiCamera Помимо стандартных утилит, камеру можно использовать программными методами. Такой подход необходим, например, при построении систем безопасности, когда нужно активизировать запись, при наступлении какого-нибудь события или по расписанию. Все скетчи на питоне, демонстрируемые далее, можно скачать как отдельно, по ссылке справа от каждого скетча, так и одним архивом — raspberry_camera_sketches.rar (16,7 KB).
|
Документация по работе с библиотекой PiCamera доступна в онлайн-режиме — picamera.readthedocs.io. Для доступа к документации в режиме оффлайн её можно установить командой:
1 sudo apt-get install python-picamera-docs Для оффлайн-установки библиотеки PiCamera доступен репозиторий. |
55 |
Для работы с камерой в языке Python, понадобится библиотека PiCamera, которая предустановлена в системе. Если по какой-то причине её нет, то установить библиотеку можно командой: |
|
56 |
1 sudo apt-get install python3-picamera |
|
57 | Важно: |
Нельзя сохранять скетчи с именем picamera.py — это сделает невозможным использование библиотеки PiCamera в Python.
|
|
58 |
Когда библиотека установлена, в скетче её необходимо импортировать: |
|
59 | Python |
1 import picamera |
|
60 |
Также, для работы с временными паузами, нужно импортировать функцию sleep() из библиотеки time: |
|
61 | Python |
1 from time import sleep |
|
62 |
Далее создается экземпляр класса PiCamera, с которым удобно работать: |
|
63 | Python |
1 camera=picamera.PiCamera() |
|
64 | На заметку: |
Справку по объекту можно получить при помощи функции help(), выполнив последовательно в оболочке Shell команды:
1 2 >>> import picamera
>>> help(picamera) Либо вызвать функцию print() из кода программы: 1 2 3 import picamera
camera=picamera.PiCamera()
print(help(camera)) |
|
65 |
Для корректного завершения работы с камерой и высвобождения занятых ресурсов, необходимо использовать метод close(). При вызове этого метода, будет также остановлена вся активность камеры — закрыто окно предпросмотра и завершена активная запись: |
|
66 | Python |
1 camera.close() |
|
67 |
В соответствии с условностями, вызывать метод close() необходимо в разделе finally блока try: |
|
68 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import picamera # Импортируем библиотеку PiCamera
from time import sleep
import datetime as dt
try:
camera = picamera.PiCamera()
# === Основной код ===
finally:
camera.close() # Корректно завершаем любую активность камеры
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
camera_001_ template.py (698 Bytes)
|
69 |
Для проверки работоспособности можно на 10 секунд вызвать окно предпросмотра (preview window): |
|
70 | Python |
1 2 3 4 5 6 7 import picamera
from time import sleep
camera = picamera.PiCamera()
camera.start_preview() # Открываем окно предпросмотра
sleep(10) # через 10 секунд
camera.stop_preview() # Закрываем окно предпросмотра |
camera_002_ preview.py (924 Bytes)
|
71 |
Для окна предпросмотра можно устанавливать прозрачность: |
|
72 | Python |
1 2 3 camera.start_preview(alpha=190) # Открываем окно предпросмотра с прозрачностью 190 (255 - не прозрачно, 0 - прозрачно)
# или прозрачность можно изменить после открытия окна просмотра
camera.preview.alpha = 100 |
|
73 |
Теперь, можно, по аналогии с утилитами raspistill и raspivid, при помощи объекта camera сделать фото с камеры: |
|
74 | Python |
1 2 3 4 5 # Если не указывать абсолютный путь, файл будет создан в папке со скетчем
camera.capture('image.jpg')
# или
# можно указать абсолютный путь для сохранения
camera.capture('/home/pi/image.jpg') |
camera_003_ capture.py (1,26 KB)
|
75 |
Или записать видео: |
|
76 | Python |
1 2 3 camera.start_recording('video.h264') # Начинаем запись и сохраняем в файл video.h264
camera.wait_recording(60) # Записываем 1 минуту, после чего
camera.stop_recording() # Завершаем запись |
camera_004_ start_recording.py (1,12 KB)
|
77 | На заметку: |
Использование нативного метода wait_recording() для указания времени записи, предпочтительнее метода sleep() библиотеки time, потому что он отслеживает ошибки записи (например, ошибка нехватки места на диске) в процессе работы. А при использовании метода sleep(), об ошибках станет известно только при остановке записи методом stop_recording().
|
|
78 |
На фото/видео и окно предпросмотра можно накладывать текст и изменять его параметры — размер, цвет, цвет фона. В аннотациях, помимо цифр и знаков, допустимы только буквы латинского алфавита, при попытке отобразить кириллический символ будет выведена ошибка. В примере, в качестве текста используется актуальное время, которое обновляется каждые 200 мс: |
|
79 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import picamera
import datetime as dt
camera = picamera.PiCamera()
camera.start_preview() # Открыть окно предпросмотра
camera.annotate_text = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Выводим дату/время
camera.annotate_text_size = 50 # Размер текста из диапазона 6...160, по-умолчанию 32
camera.annotate_foreground = picamera.Color(y=0.2, u=0, v=0) # Цвет текста - серый
camera.annotate_background = picamera.Color('black') # На черном фоне
camera.start_recording('timestamped.h264') # Начинаем запись
start = dt.datetime.now() # Обнуляем счетчик
while (dt.datetime.now() - start).seconds < 30: # Пока не прошло 30 секунд
camera.annotate_text = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Обновляем текст
camera.wait_recording(0.2) # Каждые 200 мс
camera.stop_recording() # Завершаем запись
camera.stop_preview() # Закрываем окно предпросмотра |
camera_005_ annotate.py (2,07 KB)
|
80 | На заметку: |
Особенностью использования цветов отображения текста на изображении с камеры (annotate) является различие в задании цветов для фона (annotate_background) и самого текста (annotate_foreground). Дело в том, что цвета для фона можно задавать удобными значениями Color('yellow'), Color('blue'), Color('purple') и т.д., в то время как цвет текста задается в формате YUV — Color(y=0.2, u=0, v=0). Но не стоит пугаться сложности формул конвертации RGB в YUV — в свойстве annotate_foreground поддерживается только составляющая Y, которая по сути отвечает за яркость текста. На это указывает справка к классу camera:
|
|
81 |
Объект camera позволяет задавать настройки для камеры: |
|
82 | Python |
1 2 3 4 5 6 7 8 9 10 camera.sharpness = 0 # Резкость
camera.contrast = 0 # Контрастность
camera.brightness = 50 # Яркость
camera.saturation = 0 # Насыщенность
camera.ISO = 0 # Чувствительность
camera.rotation = 0 # Поворот
camera.hflip = False # Отражение по горизонтали
camera.vflip = False # Отражение по вертикали
camera.crop = (0.0, 0.0, 1.0, 1.0) # Обрезка
camera.awb_mode = "sunlight" # Баланс белого |
|
83 |
И изменять их во времени: |
|
84 | Python |
1 2 3 4 5 camera.start_preview()
for i in range(100):
camera.brightness = i # Каждые 100 миллисекунд меняем яркость от 0 до 100
sleep(0.1)
camera.stop_preview() |
camera_006_ params_change_ brightness.py (1,83 KB)
|
85 |
Конструктор класса PiCamera позволяет задавать некоторые настройки при создании экземпляра класса PiCamera: |
|
86 | Python |
1 2 import picamera
camera = picamera.PiCamera(sensor_mode=6, framerate=24) |
|
87 | На заметку: |
Параметр sensor_mode задает одну из базовых настроек, поддерживаемых камерой на аппаратном уровне. Варианты значений и диапазоны допустимых частот кадров описаны в документации.
|
|
88 |
Перед тем, как сделать фото или записать видео, можно настроить разрешение и частоту кадров: |
|
89 | Python |
1 2 camera.resolution = (1280, 720) # Разрешение
camera.framerate = 24 # Частота кадров |
|
90 |
Причем, отдельно задать размер фото/видео можно при вызове методов capture() и start_recording(), передав нужное значение параметра resize: |
|
91 | Python |
1 2 3 camera.resolution = (1280, 720) # Разрешение по-умолчанию
camera.capture('image.jpg', resize=(320, 240)) # Изменить разрешение фото
camera.start_recording('video.h264', resize=(640, 480)) # Изменить разрешение видео |
|
92 |
К изображению камеры можно применять различные эффекты: |
|
93 | Python |
1 2 3 4 5 6 camera.start_preview()
for effect in camera.IMAGE_EFFECTS: # Поочередно получаем все доступные эффекты
camera.image_effect = effect # И применяем их к изображению
camera.annotate_text = "Effect: %s" % effect # Накладываем на изображение название примененного эффекта
sleep(5) # Можно смело использовать sleep() так как запись не ведется
camera.stop_preview() |
camera_007_ effects.py (1,72 KB)
|
94 |
Аналогичным образом можно перебрать варианты настройки баланса белого awb_mode и варианты режимов экспозиции exposure_mode: |
|
95 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 camera.start_preview()
for mode in camera.AWB_MODES: # Поочередно получаем все доступные режимы баланса белого
camera.awb_mode = mode # И задаем режим работы камеры
camera.annotate_text = "AWB mode: %s" % mode # Накладываем на изображение название режима баланса белого
sleep(5) # Можно смело использовать sleep() так как запись не ведется
# или
for mode in camera.EXPOSURE_MODES: # Выводим поочередно все доступные режимы экспозиции
camera.exposure_mode = mode # И задаем режим работы камеры
camera.annotate_text = "Exposure: %s" % mode # Накладываем на изображение название режима экспозиции
sleep(5) # Можно смело использовать sleep() так как запись не ведется
camera.stop_preview() |
camera_008_ modes_awb.py (2,26 KB)
|
96 |
При помощи метода capture_continuous(), не составит труда сгенерировать набор фотографий для Timelapse. В примере фото делается в начале каждого часа: |
|
97 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import picamera # Импортируем библиотеку PiCamera
from time import sleep
from datetime import datetime, timedelta
def wait(): # Функция расчета точной паузы между снимками
next_minute = (datetime.now() + timedelta(seconds=60)) # Добавляем минуту к текущему времени
next_minute = next_minute.replace(second=0, microsecond=0) # Обнуляем секунды/миллисекунды
now = datetime.now()
delay = ((next_minute - now).seconds +
(next_minute - now).microseconds/1000000) # Получаем разницу в секундах c точностью до микросекунд
sleep(delay) # Ждем
try:
camera = picamera.PiCamera()
camera.start_preview()
wait() # Ждем наступления новой минуты
for filename in camera.capture_continuous('img_{timestamp:%Y-%m-%d-%H-%M}.jpg'):
print('Captured %s' % filename)
wait() # Функция для точного расчета паузы между снимками, для упрощения можно заменить на sleep()
camera.stop_preview() # Закрываем окно предпросмотра
finally:
camera.close() # Корректно завершаем любую активность камеры
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
camera_009_ timelapse.py (2,00 KB)
|
98 |
Отключить мешающий светодиод можно свойством led: |
|
99 | На заметку: |
Поскольку при подключении камеры, пин управления светодиодом камеры перемещается в GPIO Малинки и становится недоступным напрямую для процессора ARM, этот метод работает только при установленной библиотеке RPi.GPIO и при установленном режиме пинов BCM — GPIO.setmode(GPIO.BCM).
|
|
100 | Python |
1 2 3 4 5 6 import RPi.GPIO as GPIO
import picamera
GPIO.setmode(GPIO.BCM)
camera = picamera.PiCamera()
camera.led = False |
|
101 |
Использование камеры из Python — библиотека PiCamera (продвинутый уровень) Библиотека PiCamera предоставляет очень широкие возможности по взаимодействию с камерой. Ниже представлены примеры решения некоторых неочевидных задач. |
|
102 |
Захват видео в несколько потоков (максимум 4). Для каждого из них, при помощи параметра resize можно устанавливать индивидуальное разрешение. Каждый поток идентифицируется параметром splitter_port (0, 1, 2, 3): |
|
103 | Python |
1 2 3 4 5 6 7 8 9 10 # Команда без параметра splitter_port по умолчанию занимает порт 1
camera.start_recording('sp_highres.h264') # Запускаем запись видео с параметрами по умолчанию
camera.start_recording('sp_lowres.h264', splitter_port=0, resize=(640, 480)) # Параллельно запускаем другой поток с уменьшенным разрешением
camera.start_recording('sp_lowres.h264', splitter_port=2, resize=(320, 240)) # Параллельно запускаем другой поток с ещё более уменьшенным разрешением
camera.wait_recording(60) # Записываем 1 минуту видео
camera.stop_recording() # Останавливаем запись всех потоков по очереди
camera.stop_recording(splitter_port=0)
camera.stop_recording(splitter_port=2) |
camera_010_ splitter_port.py (1,96 KB)
|
104 |
Разделение видеопотока и сохранение его в разные файлы. Функция бывает полезна, например, для циклической записи видеоданных равной продолжительности в определенное количество файлов, таким образом, что, после того, как будет записан последний файл, данные начнут записываться заново в первый файл (принцип работы видеорегистратора). |
|
105 | На заметку: |
Нельзя использовать данный метод для построения систем безопасности с круглосуточной записью видео — это выведет из строя накопитель, который не предназначен для таких режимов работы.
|
|
106 |
При чем возможна реализация двумя способами. При помощи split_recording(): |
|
107 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import picamera # Импортируем библиотеку PiCamera
camera = picamera.PiCamera(resolution=(640, 480)) # Задаем разрешение при инициализации
camera.start_preview() # Открываем окно предпросмотра
camera.start_recording('/home/pi/1.h264') # Начинаем записывать в файл #1
camera.wait_recording(5) # Записываем 5 секунд
startindex = 2 # Задаем начальный индекс
while(1): # Бесконечный цикл
for i in range(startindex, 11): # Будем разделять в файлы с именами от 1.h264 до 10.h264
camera.split_recording('/home/pi/%d.h264' % i) # Разделяем видеопоток
camera.wait_recording(5) # Записываем 5 секунд
startindex = 1 # После записи 10 файла возвращем индекс к началу
camera.stop_recording() # Останавливаем запись
camera.stop_preview() # Закрываем окно предпросмотра |
camera_011_01_ split_recording.py (1,96 KB)
|
108 |
И при помощи record_sequence(): |
|
109 | Python |
1 2 3 4 5 6 7 8 9 10 11 import picamera # Импортируем библиотеку PiCamera
camera = picamera.PiCamera(resolution=(640, 480)) # Задаем разрешение при инициализации
camera.start_preview() # Открываем окно предпросмотра
while(1): # Бесконечный цикл
for filename in camera.record_sequence(
'/home/pi/%d.h264' % i for i in range(1, 11)): # Будем разделять в файлы с именами от 1.h264 до 10.h264
camera.wait_recording(5) # Записываем 5 секунд
camera.stop_recording() # Останавливаем запись
camera.stop_preview() # Закрываем окно предпросмотра |
camera_011_02_ record_sequence.py (1,45 KB)
|
110 |
Постоянная циклическая запись видеопотока в буфер с последующим сохранением его в файл по заданному сигналу. При использовании этого способа можно строить системы безопасности, в которых запись потока в файл будет производиться по заданному событию. Преимуществом данного способа является сохранение видео, которое также, при необходимости, будет содержать данные, предшествующие наступлению события. |
|
111 |
В примере демонстрируется постоянная запись в буфер длительностью 20 секунд в течение 100 секунд, причем только последние 10 секунд будут сохранены в файл: |
|
112 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import sys, traceback # Импортируем библиотеки для обработки исключений
import picamera # Импортируем библиотеку по работе с камерой
import datetime as dt # Импортируем библиотеку для работы с датой/временем
from time import sleep # Импортируем функцию для работы с паузами
try:
camera = picamera.PiCamera() # Создаем экземпляр класса для работы с камерой
camera.rotation=180
stream = picamera.PiCameraCircularIO(camera, seconds=20) # Создаем буфер размером в 20 секунд
camera.annotate_text_size = 100 # Размер текста из диапазона 6...160, по-умолчанию 32
#camera.annotate_foreground = picamera.Color(y=1, u=0, v=0)
camera.annotate_background = picamera.Color('black') # На черном фоне
camera.start_recording(stream, format='h264') # Начинаем запись в буфер
i=0
while (i<100):
annotate_text = ""
if (i<95):
camera.annotate_text = "Before event (" + str(i) + ") - " # Маркируем время до наступления события
else:
camera.annotate_text = "After event (" + str(i) + ") - " # Маркируем время после наступления события
camera.annotate_text = annotate_text + dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Добавляем дату/время
camera.wait_recording(1) # Ждем секунду
i+=1
print(i) # Выводим состояние в оболочку Shell
camera.stop_recording() # Останавливаем запись
stream.copy_to("/home/pi/stream.h264", seconds=10) # Сохраняем последние 10 секунд из буфера в файл
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
camera.close() # Завершаем работу с экзепляром класса камеры
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
camera_012_ buffer.py (3,58 KB)
|
113 |
Далее в статье будет приведен пример системы безопасности с записью 15-секундного видео (5 секунд до наступления события и 10 секунд после). |
|
114 |
Пример использования камеры Raspberry Pi 3 в системах безопасности. Поскольку, тема детектирования движения и распознавания образов программным способом, будет затронута в следующих статьях, в данном примере о наступлении события движения будет сообщать датчик движения. |
|
115 |
Подробно о модуле HC-SR501, его устройстве, принципе работы и вариантах использования можно почитать в статье — Инфракрасный датчик движения HC-SR501: использование с Arduino и Raspberry Pi 3.
|
|
116 |
В качестве датчика движения будет использоваться пьезоэлектрический инфракрасный датчик движения HC-SR501. Преимущество его использования заключается в том, что напряжение его питания должно быть в диапазоне 5-20 В, а уровень логической единицы на выходе равен 3,3 В. Эти параметры позволяют подключать датчик HC-SR501 напрямую к Raspberry Pi 3 без конвертеров питания и логических уровней. |
|
118 |
Суть системы безопасности заключается в следующем — при детектировании датчиком движения в зоне действия, камера будет делать фото и отправлять его на заданный адрес электронной почты. Светодиод в схеме дублирует состояние датчика — загорается в моменты срабатывания и гаснет, когда датчик отключается. |
|
119 |
Схема подключения (принципиальная и наглядная): |
|
|
122 |
В скетче, для отслеживания срабатывания датчика, используются прерывания, реализованные в библиотеке RPi.GPIO. Также использованы материалы статьи об отправке электронной почты с вложениями на языке Python. |
|
123 |
Скетч: |
|
124 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import sys, traceback # Импортируем библиотеки для обработки исключений
import picamera # Импортируем библиотеку по работе с камерой
import datetime as dt # Импортируем библиотеку для работы с датой/временем
from datetime import timedelta
from time import sleep # Импортируем функцию для работы с паузами
# Импорт библиотек, модулей и классов для работы с почтой ========================================================================================================
import smtplib # Импортируем библиотеку по работе с SMTP
import os # Функции для работы с операционной системой, не зависящие от используемой операционной системы
# Добавляем необходимые подклассы - MIME-типы
import mimetypes # Импорт класса для обработки неизвестных MIME-типов, базирующихся на расширении файла
from email import encoders # Импортируем энкодер
from email.mime.base import MIMEBase # Общий тип
from email.mime.text import MIMEText # Текст/HTML
from email.mime.image import MIMEImage # Изображения
from email.mime.audio import MIMEAudio # Аудио
from email.mime.multipart import MIMEMultipart # Многокомпонентный объект
periodBetweenEvents = 60 # Минимальный период между отправками фото
try:
flagBusy = False # Флаг занятости - для контроля завершенности предыдущего вызова
# Переменная, предохраняющая от слишком частого срабатывания события и отправки фото
lastAction = dt.datetime.now()
lastAction = lastAction - timedelta(seconds=(periodBetweenEvents-20)) # Даем 20 секунд на калибровку датчика движения
# Функция-обработчик срабатывания датчика движения ===============================================================================================================
def detect_motion(pin): # Объявляем функцию-обработчик callback_func1
global flagBusy # Даем понять функции, что переменная не локальная, а глобальная
global camera # Даем понять функции, что переменная не локальная, а глобальная
global lastAction # Даем понять функции, что переменная не локальная, а глобальная
print("Состояние пина " + str(pin) + " изменилось " + str(GPIO.input(pin)))
GPIO.output(pinLED, GPIO.input(pin)) # Устанавливаем состояние светодиода в соответствии с состоянием датчика
print((dt.datetime.now()-lastAction).seconds)
if not flagBusy and ((dt.datetime.now()-lastAction).seconds > periodBetweenEvents): # Если флаг занятости установлен или не прошло 30 секунд пропускаем итерацию
flagBusy=True # Устанавливаем флаг занятости, чтобы дать процессу завершиться
if GPIO.input(pin): # Если наступление события (0->1), то делаем кадр
print("Отправляю фото...")
sleep(2) # Пауза нужна, чтобы объект успел попасть в кадр до момента съемки
# Сначала задаем параметры накладываемого поверх текста - дата/время кадра
camera.annotate_text = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Выводим дату/время
camera.annotate_text_size = 50 # Размер текста из диапазона 6...160, по-умолчанию 32
camera.annotate_foreground = picamera.Color(y=1.0, u=0.0, v=0.0) # Цвет текста - белый
camera.annotate_background = picamera.Color('black') # На черном фоне
camera.capture('/home/pi/image.jpg') # Осуществляем захват изображения
send_email("mymail@mail.ru", # Когда кадр сделан, его нужно отправить на электронку
"Обнаружено движение", # Тема
"Внимание! Устройством безопасности обнаружено движение. Фото во вложении.", # Текст сообшения
["/home/pi/image.jpg"]) # Список файлов
print("Фото отправлено")
lastAction = dt.datetime.now() # Дата/время последнего снимка
flagBusy=False # Возвращаем флаг, чтобы при следующем срабатывании функция была исполнена
# Функции по отправке электронной почты с вложением ==============================================================================================================
def send_email(addr_to, msg_subj, msg_text, files):
# Здесь задаются идентификационные данные учетной записи - логин, пароль =================================================================================
addr_from = "my_addr@server.ru" # Отправитель, как правило совпадает с полем логин
password = "password" # Пароль
# ========================================================================================================================================================
msg = MIMEMultipart() # Создаем сообщение
msg['From'] = addr_from # Адресат
msg['To'] = addr_to # Получатель
msg['Subject'] = msg_subj # Тема сообщения
body = msg_text # Текст сообщения
msg.attach(MIMEText(body, 'plain')) # Добавляем в сообщение текст
process_attachement(msg, files)
# Этот блок настраивается для каждого почтового провайдера отдельно, подробно - http://codius.ru/articles/280 ============================================
# Например, для yandex настройки будут следующими:
server = smtplib.SMTP_SSL('smtp.yandex.ru', 465) # Создаем объект SMTP
#server = smtplib.SMTP_SSL('smtp.server.ru', 465) # Создаем объект SMTP
#server.starttls() # Начинаем шифрованный обмен по TLS
#server.set_debuglevel(True) # Включаем режим отладки, если не нужен - можно закомментировать
server.login(addr_from, password) # Получаем доступ
server.send_message(msg) # Отправляем сообщение
server.quit() # Выходим
# ========================================================================================================================================================
def process_attachement(msg, files): # Функция по обработке списка, добавляемых к сообщению файлов
for f in files:
if os.path.isfile(f): # Если файл существует
attach_file(msg,f) # Добавляем файл к сообщению
elif os.path.exists(f): # Если путь не файл и существует, значит - папка
dir = os.listdir(f) # Получаем список файлов в папке
for file in dir: # Перебираем все файлы и...
attach_file(msg,f+"/"+file) # ...добавляем каждый файл к сообщению
def attach_file(msg, filepath): # Функция по добавлению конкретного файла к сообщению
filename = os.path.basename(filepath) # Получаем только имя файла
ctype, encoding = mimetypes.guess_type(filepath) # Определяем тип файла на основе его расширения
if ctype is None or encoding is not None: # Если тип файла не определяется
ctype = 'application/octet-stream' # Будем использовать общий тип
maintype, subtype = ctype.split('/', 1) # Получаем тип и подтип
if maintype == 'image': # Если изображение
with open(filepath, 'rb') as fp: # Открываем файл для чтения
file = MIMEImage(fp.read(), _subtype=subtype) # Используем тип MIMEImage
fp.close() # После использования файл обязательно нужно закрыть
else: # Неизвестный тип файла
with open(filepath, 'rb') as fp:
file = MIMEBase(maintype, subtype) # Используем общий MIME-тип
file.set_payload(fp.read()) # Добавляем содержимое общего типа (полезную нагрузку)
fp.close()
encoders.encode_base64(file) # Содержимое должно кодироваться как Base64
file.add_header('Content-Disposition', 'attachment', filename=filename) # Добавляем заголовки
msg.attach(file) # Добавляем файл к отправляемому сообщению
# Основной код программы =========================================================================================================================================
camera = picamera.PiCamera() # Создаем экземпляр класса для работы с камерой
camera.rotation=180
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pinLED=24 # Пин светодиода
pinSensor=15 # Пин датчика
GPIO.setup(pinLED, GPIO.OUT, initial=0) # Пин светодиода в режим OUTPUT
GPIO.setup(pinSensor, GPIO.IN) # Сигнальный пин сенсора в режим INPUT, без подтяжки
GPIO.add_event_detect(pinSensor, GPIO.BOTH, callback=detect_motion) # Назначаем функцию-обработчик (detect_motion) на детектирование события (изменение сигнала - и по фронту и по спаду)
while 1:
sleep(1) # Бесконечный цикл с итерацией раз в 10 секунд
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
camera.close() # Завершаем работу с экзепляром класса камеры
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
camera_013_ security_ sendphoto.py (15,0 KB)
|
125 |
От частого срабатывания датчика движения, пример дополнен программным «предохранителем» — переменная periodBetweenEvents — он не позволяет делать и отправлять фото чаще чем раз в 6- секунд. |
|
126 |
Письмо системы безопасности с фото
|
|
127 |
Данный пример обладает одним существенным недостатком — из-за разницы в углах охвата камеры и датчика движения, очень вероятны ситуации, когда снимок будет сделан до того, как нужный объект попадет в поле зрения камеры, либо уже после того, как объект из него удалится. Этот недостаток можно устранить разными способами:
|
|
128 |
Но библиотека PiCamera, как уже было упомянуто выше, обладает возможностью осуществлять циклическую запись видео в буфер заданного размера. Это позволяет получить доступ к видеоданным, записанным до наступления события. В следующем примере, Малинка постоянно осуществляет запись в буфер длительностью 30 секунд. При наступлении события, в файл записывается 5 секунд до наступления события и 10 секунд после его наступления. Каждое последующее событие записывается в новый файл — его имя формируется из номера по порядку и даты/времени наступившего события. |
|
129 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import sys, traceback # Импортируем библиотеки для обработки исключений
import picamera # Импортируем библиотеку по работе с камерой
import datetime as dt # Импортируем библиотеку для работы с датой/временем
from datetime import timedelta
from time import sleep # Импортируем функцию для работы с паузами
periodBetweenEvents = 60 # Минимальный период между отправками фото
try:
saveEvent = False # Флаг занятости - для контроля завершенности предыдущего вызова
# Переменная, предохраняющая от слишком частого срабатывания события и отправки фото
lastAction = dt.datetime.now()
lastAction = lastAction - timedelta(seconds=(periodBetweenEvents-10)) # Даем 20 секунд на калибровку датчика движения
startEvent = dt.datetime.now()
# Функция-обработчик срабатывания датчика движения ===============================================================================================================
def detect_motion(pin): # Объявляем функцию-обработчик callback_func1
global saveEvent # Даем понять функции, что переменная не локальная, а глобальная
global camera # Даем понять функции, что переменная не локальная, а глобальная
global lastAction # Даем понять функции, что переменная не локальная, а глобальная
global startEvent # Переменная номера файла
print("Состояние пина " + str(pin) + " изменилось " + str(GPIO.input(pin)))
GPIO.output(pinLED, GPIO.input(pin)) # Устанавливаем состояние светодиода в соответствии с состоянием датчика
print((dt.datetime.now()-lastAction).seconds)
if not saveEvent and ((dt.datetime.now()-lastAction).seconds > periodBetweenEvents): # Если флаг занятости установлен или не прошло 30 секунд пропускаем итерацию
if GPIO.input(pin): # Если наступление события (0->1), то сохраняем видео
print("Продолжаем запись 10 секунд после наступления события")
startEvent = dt.datetime.now() # Запоминаем, когда наступило событие
saveEvent = True # Устанавливаем флаг срабатывания события
# Основной код программы =========================================================================================================================================
camera = picamera.PiCamera() # Создаем экземпляр класса для работы с камерой
camera.rotation=180 # Поворачиваем изображение на 180 градусов
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pinLED=24 # Пин светодиода
pinSensor=15 # Пин датчика
GPIO.setup(pinLED, GPIO.OUT, initial=0) # Пин светодиода в режим OUTPUT
GPIO.setup(pinSensor, GPIO.IN) # Сигнальный пин сенсора в режим INPUT, без подтяжки
GPIO.add_event_detect(pinSensor, GPIO.BOTH, callback=detect_motion) # Назначаем функцию-обработчик (detect_motion) на детектирование события (изменение сигнала - и по фронту и по спаду)
camera.annotate_text_size = 50 # Размер текста из диапазона 6...160, по-умолчанию 32
stream = picamera.PiCameraCircularIO(camera, seconds=30) # Создаем буфер размером в 30 секунд
camera.start_recording(stream, format='h264') # Начинаем запись в буфер
fileNum = 1 # Начальный номер файла
while 1: # Бесконечный цикл
annotate_text = ""
if saveEvent:
annotate_text = "After event - " # Маркируем кадры после наступления события
else:
annotate_text = "Before event - " # Маркируем кадры до наступления события
camera.annotate_text = annotate_text + dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Добавляем дату/время
if saveEvent and ((dt.datetime.now()-startEvent).seconds > 10):
print("Запись события завершена, приступаем к соранению")
camera.stop_recording() # Останавливаем запись
fileName = '%03d' % fileNum + startEvent.strftime('_%Y_%m_%d_%H_%M_%S')+'.h264' # Формируем имя файла
print('Видеозапись события будет сохранена в файл '+ fileName)
stream.copy_to("/home/pi/"+fileName, seconds=15) # Сохраняем последние 10 секунд из буфера в файл
print('Событие сохранено')
stream.clear() # Очищаем буфер
camera.start_recording(stream, format='h264') # Начинаем новую запись в буфер
fileNum+=1 # Наращиваем номер для имени файла
lastAction = dt.datetime.now() # Дата/время последнего снимка
saveEvent = False # Сбрасываем флаг записи события
else:
camera.wait_recording(0.2) # Ждем 0.2 секунды
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
camera.close() # Завершаем работу с экзепляром класса камеры
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
camera_014_ security_video.py (8,74 KB)
|
Аннотации к видео показывают момент срабатывания датчика движения — Before Event (до наступления события) и After Event (после наступления события) |
131 |
Качество съемки Для оценки качества съемки, прилагаются файлы записи видео в разных режимах. Видеопоток в файлах не помещен в медиаконтейнер, поэтому видео воспроизводится с частотой 30 fps. Поместить видеопоток в медиаконтейнер можно при помощи утилиты avconv: |
|
132 |
Воспроизвести файлы *.h264 на Windows-ПК можно при помощи медиаплеера VLC. |
|
133 | На заметку: |
Во время съемки камера умышленно приводится в движение, для тестирования записи динамической картинки.
|
|
134 |
Видео записано при помощи скетча: |
|
135 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import picamera # Импортируем библиотеку PiCamera
from time import sleep
import datetime as dt
def start_video_capture(filename):
global camera
annotate=('Resolution:' + str(camera.resolution) +
', Framerate:' + str(camera.framerate) +
', Sensor Mode:' + str(camera.sensor_mode))
camera.annotate_text = annotate # Выводим дату/время
camera.annotate_text_size = 20 # Размер текста из диапазона 6...160, по-умолчанию 32
camera.annotate_foreground = picamera.Color(y=1, u=0, v=0) # Цвет текста - серый
camera.annotate_background = picamera.Color('black') # На черном фоне
camera.start_recording('/home/pi/'+filename) # Начинаем запись и сохраняем в файл
camera.wait_recording(20) # Записываем 1 минуту, после чего
camera.stop_recording() # Завершаем запись
try:
# Задаем параметры основных режимов
params=[
[1, 30, (1920, 1080), '1080p'],
[5, 45, (1296, 730), '720p'],
[6, 60, (640, 480), '480p'],
[7, 90, (640, 480), '480p']
]
camera = picamera.PiCamera()
for i in range(0,4):
camera.sensor_mode = params[i][0] # Задаем режим работы сенсора (https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes)
print(camera.sensor_mode)
camera.rotation = 180 # Разворачиваем камеру
camera.resolution = params[i][2] # Задаем разрешение
camera.framerate = params[i][1] # Задаем частоту кадров
start_video_capture(params[i][3] + str(camera.framerate) + 'fps.h264') # Начинаем запись и сохраняем в файл
finally:
camera.close() # Корректно завершаем любую активность камеры
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
136 |
В некоторых местах заметен значительный пропуск кадров. |
|
138 |
Похожие запросы:
|
|