Перспектива искажения ImageMagick
Мое требование заключается в том, чтобы в перспективе преобразовать пользовательское изображение в предварительно отрендеренное фоновое изображение (которое фактически является изображением на кадр в видео).
Проще всего было использовать ImageMagick, и я написал очень грубый простой bash-скрипт для достижения того, что мне было нужно, следующим образом:
#!/bin/bash
# @author: neurosys
# @Description: Perspective transforms and projects an alpha image
# onto a background image.
if [ $# -ne 11 ]
then
echo 'Usage: ./map_image.sh background.jpg image.png output.jpg x1 y1 x2 y2 x3 y3 x4 y4';
exit;
fi
BG=$1
IMAGE=$2
DEST=$3
TEMP='temp.png'
BG_SIZE_W=$(convert $BG -print "%w\n" /dev/null)
BG_SIZE_H=$(convert $BG -print "%h\n" /dev/null)
IMAGE_W=$(convert $IMAGE -print "%w\n" /dev/null)
IMAGE_H=$(convert $IMAGE -print "%h\n" /dev/null)
X1=$4
Y1=$5
X2=$6
Y2=$7
X3=$8
Y3=$9
X4=${10}
Y4=${11}
OFFSET=15
TRANSFORM="$OFFSET,$OFFSET, $X1,$Y1 $(($IMAGE_W+$OFFSET)),$OFFSET $X2,$Y2 $OFFSET, $(($IMAGE_H+$OFFSET)) $X3,$Y3 $(($IMAGE_W+$OFFSET)), $(($IMAGE_H+$OFFSET)) $X4,$Y4"
echo "Transform matrix: $TRANSFORM"
convert $IMAGE -background transparent -extent $BG_SIZE_W\x$BG_SIZE_H-$OFFSET-$OFFSET $TEMP
convert $TEMP -background transparent -distort Perspective "$TRANSFORM" $TEMP
convert $BG $TEMP -composite $DEST
rm -f $TEMP
Тем не менее, требуется около 4 секунд, чтобы получить желаемое изображение на моем компьютере следующим образом:
[neuro@neuro-linux ~]$ time ./map_image.sh bg.png Hp-lovecraft.jpg output.jpg 494 108 579 120 494 183 576 196 && nomacs output.jpg
Transform matrix: 15,15, 494,108 195,15 579,120 15, 267 494,183 195, 267 576,196
real 0m3.852s
user 0m3.437s
sys 0m0.037s
[neuro@neuro-linux ~]$
Порядок операций, а также параметры, которые я использую в приведенном выше сценарии ImageMagick, могут быть неоптимальными. Поэтому любые мнения или альтернативы для достижения того, что мне нужно, приветствуются.
Изображения, использованные в приведенном выше примере,
Мне интересно, есть ли способ ускорить это так, чтобы я мог генерировать кадры для видео длиной в одну минуту (25 кадров в секунду * 60 секунд) в течение нескольких секунд?
На самом деле, в случае неудачи такого подхода, я могу прибегнуть к написанию специально для этого программы OpenGL, которая, я считаю, будет намного быстрее, если использовать аппаратное обеспечение.
Немного не по теме: фоновое изображение отображается в программном обеспечении для анимации (3ds Max). В случае, если я прибегаю к написанию рендерера opengl, я могу импортировать сетку и камеру из 3ds Max и сделать это для лучшей перспективы и освещения.
Благодарю.
Редактировать:
С помощью ребят на форуме ImageMagick узкое место оказалось в первом преобразовании -extent, которое было ненужным.
В итоге я объединил все команды в одну:
convert image.png -background transparent +distort Perspective "1,1, 494,108 201,1 579,120 1, 201 494,183 201, 201 576,196" -compose DstOver -composite bg.png out.png
Он запускается за 0,6 секунды, но прозрачность почему-то не работает, поэтому выходное изображение в итоге становится только искаженным изображением с черным фоном вокруг.
Редактировать:
Кто-то на форумах ImageMagick написал очень быстрый и чистый скрипт, который сократил его до 0,13 секунды.
Вот ссылка на тот случай, если это кому-нибудь понадобится: https://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=29495&p=132182
2 ответа
Попробуйте использовать формат MPC в качестве $TEMP вместо PNG. Кодирование MPC намного намного быстрее. Он предназначен для использования в качестве временного файла, для использования с ImageMagick.
MPC фактически создает два файла, *.mpc и *.cache, поэтому вам нужно удалить оба. В вашем сценарии установите TEMP=temp.mpc и TEMPCACHE=temp.cache, а затем в конце сценария rm $TEMP TEMPCACHE
Смотрите запись MPC на странице форматов ImageMagick.
Если я получу размеры изображения, используя вашу технику, это займет около 0,4 секунды для ширины и еще 0,4 секунды для высоты. Я имею в виду, как это:
BG_SIZE_W=$(convert $BG -print "%w\n" /dev/null) # 0.48 sec
BG_SIZE_H=$(convert $BG -print "%h\n" /dev/null) # 0.48 sec
Если вы получаете ширину и высоту за один раз, это займет 0,006 секунды на моей машине:
read BG_SIZE_W BG_SIZE_H < <(identify -ping -format "%w %h" bg.png)
Я все еще смотрю на остальную часть вашего кода.