DestinationFront
@DestinationFront

Как произвольно трансформировать изображение в Python?

Есть фотография с открытым ноутбуком (!на фото экран с челкой под углом вырезан!) в высоком разрешением в формате png, скриншот экрана, координаты экрана ноутбука. Как вставить скриншот экрана в экран ноутбука?

На сколько понимаю, нужно
  • создать пустое изображение размером с большое изображение с ноутбуком
  • кропнуть скриншот
  • поместить кропнутый скриншот
  • трансформировать скриншот
  • переместить трансформированный скриншот по заданным координатам
  • поверх пустого изображения с трансформированным скриншотом разместить фото с ноутубком, у которого вырезан экран
  • Вопрос задан
  • 108 просмотров
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Скажи спасибо, что я делал такую лабораторную работу X)
Код
# -*- coding: utf-8 -*-
import sys
import numpy  # pip install numpy
import cv2  # pip install opencv-python

def loadImg(fname : str) -> numpy.ndarray:  # грузит файл
    data = numpy.fromfile(fname, dtype=numpy.uint8)
    img = cv2.imdecode(data, cv2.IMREAD_COLOR)
    if img is None:
        raise IOError("Not an image file")
    return img

class Clicker:  # класс для выбора точек на экране
    def __init__(self, name: str, image: numpy.ndarray):
        self.wnd = name
        self.image = image
        self.clicks = []
        self.markersize = 5
        self.markercolor = (255,0,255)
        cv2.namedWindow(self.wnd, cv2.WINDOW_AUTOSIZE)
        cv2.setMouseCallback(self.wnd, self._click)
    
    def draw(self):  # рисует точки на изображении и выводит их на экран
        copy = self.image.copy()
        color = self.markercolor
        radius = self.markersize
        for x,y in self.clicks:
            cv2.circle(copy, (x,y), radius, color, 1)
            cv2.line(copy, (x-radius,y), (x+radius,y), color, 1)
            cv2.line(copy, (x,y-radius), (x,y+radius), color, 1)
        cv2.imshow(self.wnd, copy)
    
    def _click(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:  # левый клик - поставить точку
            self.clicks.append((x,y))
        elif event == cv2.EVENT_RBUTTONDOWN:  # правый клик - сбросить последнюю точку
            if self.clicks:
                del self.clicks[-1]
        else:
            return
        self.draw()
    
    def close(self):
        cv2.destroyWindow(self.wnd)
    
    def __enter__(self):
        self.draw()
        return self
    
    def __exit__(self, exctype, excvalue, traceback):
        self.close()

try:
    image = loadImg('times-square.jpg')  # изображение, внутрь которого вписываем другое
    poster = loadImg('lena.png')  # изображение, которое вписываем в первое
except IOError:
    print('Ошибка загрузки файла.')
    sys.exit(1)
# эта часть только для ручного ввода координат
# если они уже есть, то это не нужно.
with Clicker('Select area', image) as clicker:
    # четыре точки ставятся строго по часовой, начиная слева-сверху 
    while len(clicker.clicks) < 4:  # пока не получили четыре точки - угла
        if cv2.waitKey(100) == 27:
            print('Отменено')
            sys.exit(0)
    pts = numpy.array(clicker.clicks, dtype=numpy.float32)  # координаты углов тут
# вписываем изображение
height, width = poster.shape[:2]
srcpoints = numpy.array([  # углы вставляемого изображения в том же порядке по часовой
    (0,0),
    (width-1, 0),
    (width-1, height-1),
    (0, height-1),
], dtype=numpy.float32)
# матрица преобразования сопоставляет четыре точки второго изображения с точками первого
# по сути, она позволяет перейти от второго изображения к первому
matrix = cv2.getPerspectiveTransform(srcpoints, pts)  # порядок аргументов важен, иначе переход будет наоборот
# применяем матрицу ко второму изображению. Но теперь надо убрать чёрные поля.
warped = cv2.warpPerspective(poster, matrix, (image.shape[1], image.shape[0]))
# делаем маску для переноса пикселей с warped на image
# мы хотим перенести только пиксели, на которые пришлись пиксели второго изображения
mask = numpy.zeros(image.shape, dtype=numpy.uint8)  # рисовать можно только на обычном изображении
# закрашиваем пиксели внутри выбранного ранее четырёхугольника
cv2.fillPoly(mask, pts.reshape(1, -1, 2).astype(numpy.int32), (1,1,1))
mask.dtype = bool  # а для переноса нам нужна логическая маска
# маска готова, переносим. numpy рулит, правда ведь?
image[mask] = warped[mask]
# показываем результат
cv2.imshow('Result', image)
cv2.waitKey()


Если коротко: находишь точки, которым надо сопоставить углы "вставыша". Перечисляешь их в том же порядке, что и эти углы. Находишь матрицу перспективного преобразования. Применяешь матрицу к вставышу, получаешь чёрное изображение, на котором вставыш расположен в нужном месте. Переносишь пиксели с этого изображения на картинку с экраном.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Maksim_64
@Maksim_64
Data Analyst
Мало конкретики, а что собственно не получается. Если говорить в целом про геометрические трансформации. Сдвинуть ну просто вектор добавить, поворот матрицу с картинкой "умножить" (не по элементно конечно, математическое умножение (dot product) )" на матрицу с sin и cos, можешь загуглить какую именно, поменять размер опять таки математическое умножение на диагональную матрицу (все элементы 0 кроме тех что по диагонали) и т.д.
opencv все это дело как и многое другое естественно поддерживает. https://docs.opencv.org/4.x/da/d6e/tutorial_py_geo... вот прямо из документации.

За одно и с нейронными сетями "косвенно" познакомишься. Геометрические трансформации в многомерном пространстве это именно то что происходит в слое нейронной сети.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы