Визуализация UIImageView с преобразованием как CALayer для редактирования видео

Я делаю приложение, которое добавляет "наклейки" к видео, воспроизводящемуся в фоновом режиме. Стикеры добавляются как imageView, который реагирует на жесты панорамирования, сжатия и поворота, масштабирования и поворота, а затем применяются к UIImageview в качестве преобразований.

Чтобы добавить ImageViews к видео, я должен отобразить их как CALayer как следующий код

- (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition size:(CGSize)videoSize {

    CGSize screenSize = self.view.frame.size;
    CGFloat scale = videoSize.width / screenSize.width; //get scale between video recorded and the screen used to edit

    //Creates parentLayer that contains videoLayer and each image layer

    for (UIImageView *imageView in [self.stickerContainer subviews]) {
        CALayer *layer = [CALayer layer];
        [layer setContents:(id)[imageView.image CGImage]];

        //Set layer frame
        CGRect imageFrame = imageView.frame;
        CGRect imageBounds = imageView.bounds;
        CGRect frame = CGRectMake(imageFrame.origin.x * scale, imageFrame.origin.y * scale, imageBounds.size.width * scale, imageBounds.size.height * scale);
        layer.frame = frame;

        //Second set transform
        CGAffineTransform transform = imageView.transform;
        layer.transform = CATransform3DMakeAffineTransform(transform);

        [layer setMasksToBounds:YES];
        [parentLayer addSublayer:layer];
    }

    composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                 videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
}

По сути, создайте слой с той же рамкой и трансформацией, что и у imageView. Но это создает странные результаты с каждым жестом

  1. Кажется, что в результате жестов панорамирования ось Y инвертирована (если изображение было слева вверху, оно отображается слева внизу)
  2. Кажется, что в результатах поворота жестов значения te b и c инвертированы (из CGAffineTransform)
  3. Жест масштабирования приводит к тому, что исходная точка винта визуализируется (поскольку исходная точка слоя совпадает.. может быть, точка привязки должна что-то делать?)

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

Я основал свой код в следующем уроке, поэтому код для создания нового видео в основном такой же

1 ответ

Так что я наконец нашел немного "хакерское" решение.. Я оставлю код и объясню это

- (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition videoSize:(CGSize)videoSize screenSize:(CGSize)screenSize stickersArray:(NSArray<UIImageView *>*)stickers{

    CGFloat videoScale = videoSize.width / screenSize.width; //get scale between video recorded and the screen used to edit

    CALayer *parentLayer = [CALayer layer];
    CALayer *videoLayer = [CALayer layer];
    parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
    videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
    //Add video layer first and then stickers
    [parentLayer addSublayer:videoLayer];
    for (UIImageView *imageView in stickers) {
        CALayer *layer = [CALayer layer];
        [layer setContents:(id)[imageView.image CGImage]];

        //First set image size with bounds of original image and center (not affected by transform)
        CGSize imageSize   = imageView.bounds.size;
        CGPoint center = imageView.center;

       //Fix offset issue by getting bottom point of imageView to a screen size proportion
        CGFloat yOffset = (center.y + imageSize.height/2) / screenSize.height;
        yOffset = 1 - yOffset; // invert screen proportion
        CGFloat scaledOriginX = (center.x - imageSize.width/2)* videoScale;
        CGRect frame = CGRectMake(scaledOriginX, videoSize.height * yOffset, imageSize.width * videoScale , imageSize.height * videoScale);
        layer.frame = frame;


        //Apply transforms but to original imageView
        CGAffineTransform transform = imageView.transform;
        //Invert original transform b value with c value
        layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake(transform.a, transform.c, transform.b, transform.d, 0, 0));



        [layer setMasksToBounds:YES];
        [parentLayer addSublayer:layer];


    }

    composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                 videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
}

Первоначально мой код был ошибочным, потому что я использовал imageView.frame (size а также origin), на которую влияет матрица преобразования, в результате чего получается странный кадр для CALayer, Вместо этого я должен был использовать imageView bounds а также center, которые не затронуты или изменены матрицей преобразования, как показывает этот ответ

Это устраняет проблемы масштабирования и исходной точки, однако проблемы с панорамированием и поворотом все еще присутствуют

Чтобы исправить проблемы с жестами панорамирования, мне пришлось вручную "инвертировать" ось Y, как показывает эта часть кода.

    //Fix offset issue by getting bottom point of imageView to a screen size propotion
    CGFloat yOffset = (center.y + imageSize.height/2) / screenSize.height;
    yOffset = 1 - yOffset;
    CGFloat scaledOriginX = (center.x - imageSize.width/2)* videoScale;
    CGRect frame = CGRectMake(scaledOriginX, videoSize.height * yOffset, imageSize.width * videoScale , imageSize.height * videoScale);
    layer.frame = frame;

Чтобы исправить инвертированное вращение, мне пришлось вручную инвертировать значения b и c преобразования

 layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake(transform.a, transform.c, transform.b, transform.d, 0, 0)); 

И это в основном все... не знаю, почему возникают проблемы с вращением и панорамированием, но это должно это исправить. Я действительно надеюсь, что у кого-то есть лучшее решение, чем у меня, и он знает, почему происходят эти два последних вопроса.

Другие вопросы по тегам