Files
autopilot/examples/transform_app.py

168 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pathlib import Path
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider
from kivy.uix.label import Label
from kivy.uix.image import Image as KivyImage
from kivy.graphics.texture import Texture
from kivy.core.window import Window
import numpy as np
from PIL import Image
import cv2
import io
class CameraTransformApp(App):
def build(self):
Window.size = (900, 700)
# Главный контейнер
main_layout = BoxLayout(orientation='vertical', padding=10, spacing=10)
# Контейнер для изображения
self.image_widget = KivyImage(size_hint=(1, 0.7))
main_layout.add_widget(self.image_widget)
# Контейнер для ползунков
controls_layout = BoxLayout(orientation='vertical', size_hint=(1, 0.3), spacing=5)
# Yaw (рыскание) - вращение вокруг вертикальной оси
yaw_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
yaw_layout.add_widget(Label(text='Рыскание (Yaw):', size_hint=(0.2, 1)))
self.yaw_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
self.yaw_slider.bind(value=self.on_slider_change)
self.yaw_label = Label(text='', size_hint=(0.2, 1))
yaw_layout.add_widget(self.yaw_slider)
yaw_layout.add_widget(self.yaw_label)
controls_layout.add_widget(yaw_layout)
# Pitch (тангаж) - наклон вперед/назад
pitch_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
pitch_layout.add_widget(Label(text='Тангаж (Pitch):', size_hint=(0.2, 1)))
self.pitch_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
self.pitch_slider.bind(value=self.on_slider_change)
self.pitch_label = Label(text='', size_hint=(0.2, 1))
pitch_layout.add_widget(self.pitch_slider)
pitch_layout.add_widget(self.pitch_label)
controls_layout.add_widget(pitch_layout)
# Roll (крен) - наклон влево/вправо
roll_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
roll_layout.add_widget(Label(text='Крен (Roll):', size_hint=(0.2, 1)))
self.roll_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
self.roll_slider.bind(value=self.on_slider_change)
self.roll_label = Label(text='', size_hint=(0.2, 1))
roll_layout.add_widget(self.roll_slider)
roll_layout.add_widget(self.roll_label)
controls_layout.add_widget(roll_layout)
main_layout.add_widget(controls_layout)
# Создаем тестовое изображение с сеткой
self.original_image = self.create_test_image()
self.update_image()
return main_layout
def create_test_image(self):
"""Создает тестовое изображение с сеткой и текстом"""
img = cv2.imread(Path('images') / 'photo_610.png')
return img
def get_rotation_matrix(self, yaw, pitch, roll):
"""Вычисляет матрицу поворота 3D"""
# Конвертируем в радианы
yaw = np.radians(yaw)
pitch = np.radians(pitch)
roll = np.radians(roll)
# Матрица поворота вокруг Z (yaw)
Rz = np.array([
[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]
])
# Матрица поворота вокруг Y (pitch)
Ry = np.array([
[np.cos(pitch), 0, -np.sin(pitch)],
[0, 1, 0],
[np.sin(pitch), 0, np.cos(pitch)]
])
# Матрица поворота вокруг X (roll)
Rx = np.array([
[1, 0, 0],
[0, np.cos(roll), -np.sin(roll)],
[0, np.sin(roll), np.cos(roll)]
])
# Комбинируем повороты: R = Rz * Ry * Rx
R = Rx @ Ry @ Rz
return R
def apply_perspective_transform(self, img, yaw, pitch, roll):
"""Применяет перспективное преобразование к изображению"""
h, w = img.shape[:2]
# Параметры камеры
focal_length = w
distance = w
# Матрица камеры
camera_matrix = np.array([
[focal_length / 2, 0, w / 2],
[0, focal_length / 2, h / 2],
[0, 0, 1]
])
# Получаем матрицу поворота
R = self.get_rotation_matrix(yaw, pitch, roll)
T = np.array([
[1, 0, 0.2],
[0, 1, 0.1],
[0, 0, 2],
])
# Создаем матрицу трансформации
H = camera_matrix @ R @ T @ np.linalg.inv(camera_matrix)
# Применяем преобразование
try:
result = cv2.warpPerspective(img, H, (w, h),
borderMode=cv2.BORDER_CONSTANT,
borderValue=(50, 50, 50))
return result
except:
return img
def update_image(self):
"""Обновляет изображение с текущими параметрами"""
yaw = self.yaw_slider.value
pitch = self.pitch_slider.value
roll = self.roll_slider.value
# Применяем трансформацию
transformed = self.apply_perspective_transform(
self.original_image, yaw, pitch, roll
)
# Конвертируем в текстуру Kivy
buf = cv2.flip(transformed, 0).tobytes()
texture = Texture.create(size=(transformed.shape[1], transformed.shape[0]),
colorfmt='bgr')
texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
self.image_widget.texture = texture
def on_slider_change(self, instance, value):
"""Обработчик изменения ползунков"""
self.yaw_label.text = f'{int(self.yaw_slider.value)}°'
self.pitch_label.text = f'{int(self.pitch_slider.value)}°'
self.roll_label.text = f'{int(self.roll_slider.value)}°'
self.update_image()
if __name__ == '__main__':
CameraTransformApp().run()