Наблюдение за табло. Часть 2. Удаленное управление камерой


В этой части мы научимся работать с камерой телефона. Скрипт на питоне, который делает снимки и копирует их на компьютер.

Содержание цикла



Как я уже писал в первой части, изначально планировал написать приложение для телефона – прям apk – чтобы поставить и оно делало что надо. Но в процессе оказалось, что для одиночного снимка камерой телефона надо самому (!) человеку нажимать, чтобы получить фото. Вообще, можно обойти и делать серию снимков автоматом, но это будет надо много кода.

А что если зайти с другой стороны? Все равно телефон постоянно будет подключен к ноутбуку – пусть ноутбук питоном и adb и управлять им? Понятно, дав сначала все необходимые разрешения в режимах разработчика и режиме отладки?

Да, оказывается это возможно и не так уж сложно. После нескольких попыток и версий родился скрипт, который через определенный промежуток времени делает снимки и копирует их на ноутбук. Код прокомментирован

import subprocess
import time
import os
from datetime import datetime

# ===== НАСТРОЙКИ =====
ADB_PATH = r"C:\Program Files (x86)\Android\android-sdk\platform-tools\adb.exe"
INTERVAL = 60  # секунд между снимками
OUTPUT_DIR = r"C:\job\cv\foto"  # папка для фото
# ====================

def check_adb():
    """Проверка доступности ADB"""
    try:
        result = subprocess.run([ADB_PATH, "devices"], capture_output=True, text=True)
        if "device" not in result.stdout:
            print("Устройство не найдено! Проверьте подключение телефона.")
            return False
        return True
    except:
        print("ADB не найден! Проверьте путь к ADB.")
        return False

def cmd(command):
    """Выполнение ADB команды (исправлено для Windows)"""
    try:
        # Разбиваем команду на части для избежания проблем с кавычками
        parts = command.split()
        if parts[0] == 'shell':
            # Для shell команд используем особый подход
            shell_cmd = ' '.join(parts[1:])
            result = subprocess.run([ADB_PATH, "shell", shell_cmd], 
                                  capture_output=True, text=True)
            return result
        else:
            result = subprocess.run([ADB_PATH] + parts, 
                                  capture_output=True, text=True)
            return result
    except Exception as e:
        print(f"Ошибка выполнения команды: {e}")
        return None

def ensure_dir():
    """Создание папки для фото"""
    try:
        os.makedirs(OUTPUT_DIR, exist_ok=True)
        print(f"Фото будут сохраняться в: {OUTPUT_DIR}")
        return True
    except Exception as e:
        print(f"Ошибка создания папки: {e}")
        return False

def count_photos():
    """Подсчет количества фото в папке Camera"""
    result = cmd("shell ls /sdcard/DCIM/Camera/ | grep -c '.jpg'")
    if result and result.stdout:
        return result.stdout.strip()
    return "0"

def get_latest_photo():
    """Получение пути к последнему фото"""
    # Используем ls с сортировкой по времени
    result = cmd("shell ls -t /sdcard/DCIM/Camera/*.jpg 2>/dev/null | head -1")
    if result and result.stdout.strip():
        return result.stdout.strip()
    return None

print("? Автоматическая съемка запущена")
print(f"Интервал: {INTERVAL} сек")
print(f"Папка сохранения: {OUTPUT_DIR}")
print("Для остановки нажмите Ctrl+C")
print("-" * 40)

# Проверяем ADB и создаем папку
if not check_adb():
    exit(1)
if not ensure_dir():
    exit(1)

# Проверяем доступность папки Camera
test_result = cmd("shell ls /sdcard/DCIM/Camera/")
if test_result and test_result.returncode == 0:
    print("Доступ к папке Camera есть")
    # Показываем список фото
    print("Фото на телефоне:")
    print(test_result.stdout)
else:
    print("Не удается получить доступ к папке Camera!")
    exit(1)

photo_num = 1
last_photo_count = 0

try:
    while True:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        print(f"\n[{timestamp}] Снимок #{photo_num}")
        
        # Получаем количество фото ДО съемки
        before_count = count_photos()
        print(f"? Фото в папке до съемки: {before_count}")
        
        # Разблокировка экрана
        print("Разблокировка...")
        cmd("shell input keyevent 26")  # Включение
        time.sleep(1)
        cmd("shell input swipe 300 1000 300 300")  # Свайп вверх
        time.sleep(2)
        
        # Запуск камеры
        print("Запуск камеры...")
        cmd("shell am start -a android.media.action.STILL_IMAGE_CAMERA")
        time.sleep(3)
        
        # Съемка
        print("Съемка...")
        cmd("shell input keyevent 27")  # Затвор
        time.sleep(4)  # Ждем сохранения
        
        # Закрываем камеру (несколькими способами)
        print("Закрытие камеры...")
        cmd("shell input keyevent 4")  # Назад
        time.sleep(1)
        cmd("shell input keyevent 4")  # Еще раз назад
        time.sleep(1)
        
        # Принудительно останавливаем приложение камеры
        cmd("shell am force-stop com.android.camera2")
        time.sleep(1)
        
        # Блокировка экрана
        print("Блокировка...")
        cmd("shell input keyevent 26")
        
        # Ждем обновления галереи
        time.sleep(2)
        
        # Получаем количество фото ПОСЛЕ съемки
        after_count = count_photos()
        print(f"Фото в папке после съемки: {after_count}")
        
        # Ищем новое фото
        print("Поиск нового фото...")
        
        # Получаем последнее фото
        latest_photo = get_latest_photo()
        
        if latest_photo:
            print(f"Найдено фото: {latest_photo}")
            
            # Копируем на компьютер
            local_path = os.path.join(OUTPUT_DIR, f"photo_{timestamp}.jpg")
            
            # Используем pull с правильными путями
            pull_cmd = [ADB_PATH, "pull", latest_photo, local_path]
            result = subprocess.run(pull_cmd, capture_output=True, text=True)
            
            if result.returncode == 0 and os.path.exists(local_path):
                file_size = os.path.getsize(local_path) / 1024
                print(f"Сохранено: {local_path} ({file_size:.1f} KB)")
                photo_num += 1
                last_photo_count = int(after_count) if after_count.isdigit() else 0
            else:
                print("Ошибка при копировании")
                print(f"Ошибка: {result.stderr}")
        else:
            print("Не найдено фото в папке Camera")
        
        # Статистика
        print(f"? Всего снимков: {photo_num-1}")
        
        # Ожидание
        if photo_num > 1:
            print(f"⏳ Ожидание {INTERVAL} сек до следующего снимка...")
            for i in range(INTERVAL, 0, -1):
                print(f"\r⏳ Следующий снимок через {i:3d} сек...", end='', flush=True)
                time.sleep(1)
            print()
        else:
            # Если первый снимок не удался, ждем меньше
            time.sleep(10)
        
except KeyboardInterrupt:
    print("\n\n Остановлено пользователем")
    print(f"Всего сделано снимков: {photo_num-1}")
    print(f"Фото сохранены в: {OUTPUT_DIR}")
    
# Показываем сохраненные фото
if photo_num > 1:
    print("\nСохраненные файлы:")
    for f in os.listdir(OUTPUT_DIR):
        if f.endswith('.jpg'):
            print(f"  - {f}")
Получилось:
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, распознавание, python



Файлы или база данных – сравним?
Урок 10. Массивы Java
Урок 16. Пространства имен .NET