Гид по технологиям

Создание панорам с помощью Python и OpenCV

6 min read Computer Vision Обновлено 19 Dec 2025
Панорамы с Python и OpenCV
Панорамы с Python и OpenCV

Панорамная съёмка зданий с логотипом Python

Введение

Панорамная фотография — приём, позволяющий захватить более широкий угол обзора, чем одна фотография. Обычно это делается путём сшивки нескольких перекрывающихся кадров в одно изображение. OpenCV предоставляет готовые инструменты для автоматической сшивки изображений; связка Python + OpenCV позволяет автоматизировать процесс и интегрировать его в пайплайны обработки изображений.

Кратко: чтобы получить корректную панораму, нужны фотографии с достаточной зоной перекрытия и хорошими ключевыми особенностями (контуры, текстуры, линии). Дальше идёт стандартный набор шагов: загрузить, при необходимости привести к одному размеру, сшить, обрезать чёрные/пустые области, посмотреть и сохранить.

Что вам понадобится

  • Python 3.7+ (рекомендуется виртуальное окружение)
  • Библиотека opencv-contrib-python (содержит модуль Stitcher)
  • Набор перекрывающихся изображений (форматы: .jpg/.png или другие)

В этом материале показаны рабочие функции и рекомендации по отладке.

Установка и подготовка окружения

Откройте терминал в вашем виртуальном окружении и установите OpenCV:

pip install opencv-contrib-python

Затем создайте новый Python-скрипт и импортируйте стандартные модули.

Импорт необходимых библиотек

Импортируйте cv2 и os. OS используется для навигации по файловой системе.

import cv2
import os
from typing import List, Optional

Примечание: модуль os входит в стандартную библиотеку Python и не требует установки.

Загрузка изображений из папки

Создадим функцию, которая загружает изображения из указанной папки и возвращает список NumPy-матриц. По умолчанию будем смотреть на расширения .jpg и .png, но можно расширить список поддерживаемых форматов.

def load_images(folder_path: str) -> List:
    """Загружает все изображения из папки и возвращает список изображений OpenCV."""
    images = []
    for filename in sorted(os.listdir(folder_path)):
        if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
            path = os.path.join(folder_path, filename)
            img = cv2.imread(path)
            if img is None:
                print(f"Не удалось загрузить изображение: {path}")
                continue
            images.append(img)
    return images

Советы:

  • Сортировка имён файлов помогает при последовательной съёмке (лево->право).
  • Проверяйте, что cv2.imread вернул ненулевой объект: при проблеме с форматом или правами доступа будет None.

Масштабирование изображений для единообразия и ускорения

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

def resize_images(images: List, width: int, height: int) -> List:
    resized_images = []
    for img in images:
        resized = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA)
        resized_images.append(resized)
    return resized_images

Подсказка: для очень больших изображений сначала уменьшите их до удобного размера (например, ширина 800–1600 px) — это сильно экономит время.

Сшивка изображений с помощью Stitcher

OpenCV предоставляет класс Stitcher. Вот функция-обёртка, возвращающая итоговое изображение или None при ошибке.

def stitch_images(images: List) -> Optional:
    """Склеивает список изображений в панораму. Возвращает итоговую картинку или None."""
    stitcher = cv2.Stitcher_create() if hasattr(cv2, 'Stitcher_create') else cv2.Stitcher.create()
    status, stitched = stitcher.stitch(images)
    if status == cv2.Stitcher_OK:
        return stitched
    else:
        print(f"Ошибка сшивки: код статуса {status}")
        return None

Заметки:

  • В различных версиях OpenCV API создания Stitcher может отличаться; код выше пытается поддержать оба варианта.
  • Статус возвращается как целое: cv2.Stitcher_OK означает успех.

Обрезка итоговой панорамы

После сшивки часто остаются чёрные или пустые области по краям. Один из простых способов — найти контуры непустых областей и обрезать по наибольшему.

def crop_image(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    if not contours:
        return image
    # Найдём наибольший контур по площади
    max_cnt = max(contours, key=cv2.contourArea)
    x, y, w, h = cv2.boundingRect(max_cnt)
    cropped = image[y:y+h, x:x+w]
    return cropped

Важно: если параметры порога неверны, можно использовать morphological операции (open/close) перед поиском контуров.

Предпросмотр и сохранение результата

Функция для интерактивного просмотра и сохранения итоговой панорамы:

def preview_and_save_image(image, folder_path, folder_name):
    cv2.namedWindow('Stitched Image', cv2.WINDOW_NORMAL)
    cv2.imshow('Stitched Image', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    output_filename = os.path.join(folder_path, folder_name + '_panorama.jpg')
    success = cv2.imwrite(output_filename, image)
    if success:
        print('Панорама сохранена:', output_filename)
    else:
        print('Не удалось сохранить панораму:', output_filename)

Если вы запускаете скрипт на сервере без GUI, пропустите cv2.imshow и просто сохраняйте файл.

Контрольный конвейер: сшить всю папку

Полная функция, объединяющая шаги загрузки, масштабирования, сшивки, обрезки и сохранения:

def stitch_folder(folder_path, width=800, height=800):
    images = load_images(folder_path)
    if len(images) < 2:
        print('Недостаточно изображений в папке:', folder_path)
        return

    resized = resize_images(images, width, height)
    stitched = stitch_images(resized)
    if stitched is None:
        print('Сшивка не удалась для папки:', folder_path)
        return

    cropped = crop_image(stitched)
    folder_name = os.path.basename(folder_path.rstrip(os.sep))
    preview_and_save_image(cropped, folder_path, folder_name)

Вызов:

stitch_folder('sample_images')

Практические рекомендации по съёмке

  • Держите камеру стабильно и вращайте её вокруг опорной оси объектива (по возможности используйте штатив и панорамную головку).
  • Снимайте с 20–40% перекрытием между кадрами.
  • Старайтесь, чтобы освещение везде было однородным; резкие изменения света ухудшают результаты.

Тестирование программы

Соберите набор изображений с перекрытием и поместите их в папку sample_images. Примеры тестовых изображений используются в репозитории, упомянутом в оригинальной статье.

Сцена с холмом — кадр 1

Сцена с домом и частично видимым холмом — кадр 2

Результат может выглядеть примерно так:

Результирующая панорама, полученная программой

В примере для создания панорамы использовалось девять изображений.

Когда сшивка даёт плохой результат (когда метод не сработает)

  • Слишком малое перекрытие (<10%).
  • Однообразные текстуры без ключевых точек (пустое небо, ровный пляж).
  • Сценарии с сильным движением объектов (люди, машины) между кадрами.
  • Заметные дисторсии объектива без коррекции.

В таких случаях попробуйте увеличить перекрытие, применить коррекцию дисторсии или использовать ручные режимы совмещения.

Альтернативные подходы

  • Использовать специализированные инструменты: Hugin (open-source) или коммерческие плагины, которые дают более гибкое выравнивание и экспозицию.
  • Объединять с более мощными библиотеками фич-мэппинга (например, SIFT/ORB вручную + RANSAC + warping), если вам нужен контроль над промежуточными шагами.
  • Применять многомасштабные алгоритмы (pyramid blending) для более плавных швов.

Быстрые эвристики и контроль качества

  • Меньше изображений высокого разрешения — быстрее и стабильнее: 800–1600 px по ширине обычно достаточно для прототипов.
  • Если Stitcher возвращает непредсказуемые артефакты, проверьте порядок изображений и их перекрытия.
  • Используйте гистограммы или выравнивание экспозиции перед сшивкой, если кадры сильно различаются по яркости.

Критерии приёмки

  • Скрипт успешно читает и загружает все валидные изображения из папки.
  • Для набора из двух и более перекрывающихся изображений функция stitch_folder сохраняет файл *_panorama.jpg в исходной папке.
  • Для некорректных входных данных скрипт выводит понятное сообщение об ошибке и не падает с исключением.

Типичные ошибки и способы их устранения

  • “Stitching failed” — проверьте перекрытие, порядок, размеры и наличие ключевых точек. Увеличьте разрешение или добавьте больше перекрытия.
  • Пустые чёрные области по краям — используйте crop_image и/или morphology для очистки маски.
  • cv2.imread возвращает None — проверьте путь к файлу и права доступа.

Чек-лист перед запуском

  • Виртуальное окружение активировано и установлен opencv-contrib-python
  • В папке не меньше двух изображений с расширениями .jpg/.png
  • Изображения имеют достаточное перекрытие
  • При работе на сервере отключён вызов cv2.imshow или предусмотрена проверка среды

Мини-методология для интеграции в пайплайн

  1. Приём входных изображений → 2. Предобработка (коррекция экспозиции, деформация) → 3. Масштабирование → 4. Сшивка → 5. Обрезка и очистка швов → 6. Сохранение и логирование метаданных (список файлов, время обработки).

Небольшой путь принятия решений (Mermaid)

flowchart TD
    A[Есть ли минимум 2 изображения?] -->|Нет| B[Остановить: уведомить пользователя]
    A -->|Да| C[Проверить перекрытие]
    C -->|Хорошо| D[Масштабировать и запустить Stitcher]
    C -->|Плохо| E[Порекомендовать пересъём]
    D -->|Успех| F[Обрезать и сохранить]
    D -->|Ошибка| G[Попытаться с исходными размерами или сообщить об ошибке]

Глоссарий (одна строка для терминов)

  • Ключевые точки — точки изображения, где алгоритмы находят уникальные дескрипторы для сопоставления.
  • RANSAC — стохастический алгоритм оценки модельных параметров в присутствии выбросов.
  • Stitcher — модуль OpenCV для автоматической сшивки изображений.

Заключение

Создание панорам с помощью Python и OpenCV — надёжный и гибкий способ автоматизировать сбор широкоугольных изображений. Этот рабочий набор функций покрывает типичный поток: загрузка, приведение к единому размеру, сшивка, обрезка и сохранение. При сложных случаях используйте ручную настройку ключевых точек, альтернативные инструменты или пред- и постобработку для улучшения качества.

Важно: тестируйте с реальными наборами изображений и документируйте параметры (размеры, порядок, экспозиция) для повторяемости результатов.

Краткие выводы

  • Для стабильной сшивки нужны перекрывающиеся кадры с заметными особенностями.
  • Масштабирование ускоряет работу и повышает стабильность.
  • При проблемах используйте альтернативы: Hugin, ручные шаги feature+RANSAC или более сложное блендинг-слияние.

В случае вопросов по конкретным наборам изображений или ошибкам присылайте логи и примеры — можно детально разобрать кейс.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как кадрировать видео на Android — Google Photos и Samsung
Руководство

Как кадрировать видео на Android — Google Photos и Samsung

Как удалить RDStealer и защитить RDP
Кибербезопасность

Как удалить RDStealer и защитить RDP

Выключить или перезапустить Mac через Terminal
macOS

Выключить или перезапустить Mac через Terminal

Выйти из iOS Public Beta и откатиться на стабильную
iOS

Выйти из iOS Public Beta и откатиться на стабильную

Как открыть папку Applications на Mac
macOS

Как открыть папку Applications на Mac

Как обойти лимит ChatGPT 4o — бесплатные альтернативы
Инструменты ИИ

Как обойти лимит ChatGPT 4o — бесплатные альтернативы