nlve/tests/imagecrop.sh
4aiman c18b695b89 Своё кадрирование картинки доходит до предпросмотра и ролика
Окно «Кадрирование и поворот» показывало картинку уменьшенной и
сдвинутой, а в кадре она шла во весь экран. В уплощении преобразование
ставилось только обычному видео: у картинки своя ветка, и там его не
было вовсе. Кусок из картинки делался по жёсткой цепочке «вписать целиком
плюс поля», про клип она ничего не знала.

Кусок и так делается заново, поэтому цепочка запекается прямо в него —
тем же TransformFilter, которым считают видео и само окно. Разными путями
считать одно и то же нельзя: окно обещает ровно эту строку и показывает
её текстом.

Кадрирование входит в имя файла заготовки: одна картинка с разным
увеличением — разные куски, иначе второй клип получил бы из кэша чужой.
Для этого у куска заведено отдельное поле GenFilter: Filter применяется к
исходнику при пересборке, и смешать их значило бы кадрировать дважды.

Проверки: flattest сверяет, что заявка несёт цепочку и отдельный путь;
tests/imagecrop.sh проходит всю дорогу — делает картинку, собирает
секунду ролика и смотрит на точки готового кадра. На прежней сборке
картинка выходит во весь кадр, обе падают.
2026-10-16 15:42:49 +03:00

111 lines
5.6 KiB
Bash
Executable file
Raw 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.

#!/bin/sh
# Своё кадрирование картинки доходит до готового ролика.
#
# Окно «Кадрирование и поворот» показывало картинку уменьшенной и
# сдвинутой, а в предпросмотре и в ролике она шла во весь кадр. Причина
# была в уплощении: преобразование ставилось только обычному видео, а у
# картинки своя ветка, и там его не было вовсе. Кусок из картинки делался
# по жёсткой цепочке «вписать целиком плюс поля», про клип она ничего не
# знала.
#
# Проверяется сквозь всю дорогу: картинка делается на месте, кладётся
# одним клипом с уменьшением и смещением вправо, собирается секунда
# ролика. В готовом кадре смотрим на точки: слева картинки быть не
# должно, справа — должна.
#
# Запуск: tests/imagecrop.sh [<путь к nlvetool>]
set -e
cd "$(dirname "$0")/.."
TOOL=${1:-$PWD/tests/nlvetool}
WORK=$PWD/test/картинка
FF=${FFMPEG:-/home/chaiman/bin/ffmpeg}
[ -x "$TOOL" ] || { echo "нет $TOOL — собери: ./build.sh test"; exit 2; }
[ -x "$FF" ] || FF=ffmpeg
rm -rf "$WORK"
mkdir -p "$WORK"
# Картинка 900x1200, как у настоящей рекламной вертикали: сплошной цвет,
# чтобы по точке было видно, попала она в кадр или нет.
"$FF" -hide_banner -loglevel error -y -f lavfi -i color=c=green:s=900x1200 \
-frames:v 1 "$WORK/кадр.png"
python3 - "$WORK" <<'PY'
import json, sys, os
W = sys.argv[1]
кадр = {'valid': True, 'kind': 'video', 'has_video': True,
'width': 1920, 'height': 1080, 'fps_num': 30, 'fps_den': 1,
'video_codec': 'h264', 'pix_fmt': 'yuv420p',
'has_audio': True, 'audio_codec': 'aac',
'sample_rate': 48000, 'channels': 2, 'video_bitrate': 8000000}
проект = {
'version': 1, 'next_id': 100, 'master': кадр,
'pool': [{'id': 'm1', 'path': os.path.join(W, 'кадр.png'),
'normalized_path': '',
'info': {'valid': True, 'kind': 'image', 'has_video': True,
'video_codec': 'png', 'width': 900, 'height': 1200,
'fps_num': 25, 'fps_den': 1, 'pix_fmt': 'rgb24',
'duration': 0.0, 'has_audio': False}}],
'tracks': [
{'id': 't1', 'name': 'V1', 'kind': 'video', 'clips': [
{'id': 'c1', 'media': 'm1', 'kind': 'image',
'src_in': 0.0, 'tl_start': 0.0, 'duration': 1.0,
'edge_in': 'snap', 'edge_out': 'snap', 'group': '',
'volume': 1.0, 'opacity': 1.0, 'fade_in': 0.0, 'fade_out': 0.0,
# Уменьшить вдвое и сдвинуть вправо — как в окне кадрирования.
'transform': {'fit': 'fit', 'rotation': 0, 'flip_h': False,
'flip_v': False, 'zoom': 0.5, 'offset_x': 0.6,
'offset_y': 0.0},
'text': ''}]},
{'id': 't2', 'name': 'A1', 'kind': 'audio', 'clips': []}
],
'settings': {}
}
json.dump(проект, open(os.path.join(W, 'картинка.nlve'), 'w'),
ensure_ascii=False)
print(' картинка 900x1200, кадр проекта 1920x1080, увеличение 50%,'
' смещение вправо 60%')
PY
OUT=$WORK/ролик.mkv
"$TOOL" render "$WORK/картинка.nlve" "$OUT" >"$WORK/сборка.log" 2>&1 ||
{ echo " сборка не прошла, смотри $WORK/сборка.log"; tail -5 "$WORK/сборка.log"; exit 1; }
"$FF" -hide_banner -loglevel error -y -i "$OUT" -frames:v 1 "$WORK/готовый.png"
python3 - "$WORK" <<'PY'
import sys, os
from PIL import Image
W = sys.argv[1]
im = Image.open(os.path.join(W, 'готовый.png')).convert('RGB')
ш, в = im.size
print(' кадр ролика: %dx%d' % (ш, в))
def зелено(точка):
r, g, b = точка
return g > 80 and g > r + 40 and g > b + 40
# При увеличении 50% и смещении вправо 60% картинка занимает примерно
# правую пятую часть кадра по ширине и среднюю половину по высоте
# (цепочка сборки: scale=406:540,pad=1920:1080:1514:270). Поэтому слева и
# у самого верха обязано быть чёрное поле, а в правой части — картинка.
слева = im.getpixel((int(ш * 0.12), в // 2))
справа = im.getpixel((int(ш * 0.90), в // 2))
верх = im.getpixel((ш // 2, 6))
print(' слева %s, справа %s, вверху %s' % (слева, справа, верх))
плохо = 0
if зелено(слева):
print(' ПЛОХО: картинка слева — смещение не применилось')
плохо = 1
if not зелено(справа):
print(' ПЛОХО: справа картинки нет — она встала не туда')
плохо = 1
if зелено(верх):
print(' ПЛОХО: картинка достаёт до верха — уменьшение не применилось')
плохо = 1
if плохо:
raise SystemExit(1)
print(' ok: картинка уменьшена и сдвинута вправо, как в окне кадрирования')
PY