Автоматична генерація скріншотів (+HDR). Скрипт для PowerShell

Нові коментарі

Нова тема   Відповісти
Автор Повідомлення
4br4h4m 
VIP


З нами з: 29.04.22
Востаннє: 28.06.25
Повідомлень: 479

2023-09-11 02:04  
Вітаю.
Ще раніше помічав, що у релізах з HDR люди часто роблять не зовсім коректні скріншоти через особливості "глибини кольору".

https://toloka.to/t672390
https://toloka.to/t670763
https://toloka.to/t670199

В мене були аналогічні проблеми з PotPlayer, тому шукав альтернативу, бажано взагалі напівавтоматизовану.

В результаті знайшовся такий скрипт:
https://gist.github.com/lambdan/70a36e53b90ebbff97bc9e73b2fa4414

Трішки його підправив і вирішив викласти.
Шляхи до Info.exe (CLI) і ffmpeg.exe вкажіть свої.
Скачати останні офіційні версії можна тут
зберегти як hdr.ps1 і запускати так:
powershell -ExecutionPolicy Bypass -File d:\hdr.ps1 "The.Hurt.Locker.2008..4K.2160p.x265.10bit.HDR10+.Ukr.Eng.Sub.Eng."

Можна задати параметр how_many фіксованим значенням.
PowerShell скрипт

# For batch:
# foreach($f in Get-ChildItem .) { .\hdr.ps1 $f }

$infile = $Args[0] # Input video

# Get some video length info using Info
$Info = "d:\Portable\FFMpeg\MediaInfo_CLI_23.07_Windows_x64\Info.exe"
$ffmpeg = "d:\Portable\FFMpeg\ffmpeg-2023-09-07-git-9c9f48e7f2-full_build\bin\ffmpeg.exe"
#$length_in_ms = $Info --Output='General;%Duration%' "$infile" | Out-String
$length_in_ms = & $Info --Language=raw --Full --Inform='Video;%Duration%' "$infile" | Out-String
#Info";%Duration/String4%"
$length_in_secs = $length_in_ms/1000

# Create output folder
$outfolder = "./screenshots/"
If(!(test-path $outfolder)) {
md $outfolder
}

# Create this many screenshots, or base it on video length
#$how_many = 20 # maybe make this the 2nd arg in the future?
$how_many = [math]::Round($length_in_secs/100)
Write-Host "Will create $how_many screenshots"

$used_seconds = @()
$i = 1
while($i -le $how_many) {
# Get a random second of the video that hasnt been already used
$random_sec = get-random -maximum $length_in_secs
$rounded = [math]::Round($random_sec)
if ($used_seconds.Contains($rounded) -eq $True) {
while ($used_seconds.Contains($rounded) -eq $True) {
#Write-Host $rounded "already used"
#Write-Host $used_seconds
$random_sec = get-random -maximum $length_in_secs
$rounded = [math]::Round($random_sec)
}
#Write-Host "Found unused" $rounded
}
$used_seconds += $rounded

# Outfile that screenshot will be saved as
$outfile = $outfolder + $infile + "_" + $rounded + ".png"

Write-Host $i/$how_many $outfile
#$result = & $ffmpeg -loglevel error -ss $rounded -i "$infile" -frames:v 1 $outfile | Out-String
& $ffmpeg -loglevel error -ss $rounded -i "$infile" -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p -frames:v 1 $outfile

$i++
}
Результат використання скрипта можна побачити тут:
https://toloka.to/t672395

Для не HDR прибрати оце:
Цитата:
-vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p


Використана інфа:
https://stackoverflow.com/questions/70544342/ffmpeg-why-is-output-video-contrast-brightness-too-bright
https://stackoverflow.com/questions/19091771/how-to-find-duration-of-a-video-file-using-Info-in-seconds-or-other-formats
4br4h4m 
VIP


З нами з: 29.04.22
Востаннє: 28.06.25
Повідомлень: 479

2025-04-26 13:54  
HDR для нової версії FFMPEG (7+):
PowerShell

# Requires 'ffmpeg' and 'Info' in path

# For batch:
# foreach($f in Get-ChildItem .) { .\random_screenshots.ps1 $f }

$infile = $Args[0] # Input video

# Get some video length info using Info
$Info = "d:\FFMpeg\MediaInfo_CLI_23.07_Windows_x64\Info.exe"
$ffmpeg = "d:\FFMpeg\ffmpeg-7.1.1-full_build\bin\ffmpeg.exe"
#$length_in_ms = $Info --Output='General;%Duration%' "$infile" | Out-String
$length_in_ms = & $Info --Language=raw --Full --Inform='Video;%Duration%' "$infile" | Out-String
#Info";%Duration/String4%"
$length_in_secs = $length_in_ms/1000

# Create output folder
$outfolder = "./screenshots/"
If(!(test-path $outfolder)) {
md $outfolder
}

# Create this many screenshots, or base it on video length
$how_many = 10 # maybe make this the 2nd arg in the future?
#$how_many = [math]::Round($length_in_secs/100)
Write-Host "Will create $how_many screenshots"

$used_seconds = @()
$i = 1
while($i -le $how_many) {
# Get a random second of the video that hasnt been already used
$random_sec = get-random -maximum $length_in_secs
$rounded = [math]::Round($random_sec)
if ($used_seconds.Contains($rounded) -eq $True) {
while ($used_seconds.Contains($rounded) -eq $True) {
#Write-Host $rounded "already used"
#Write-Host $used_seconds
$random_sec = get-random -maximum $length_in_secs
$rounded = [math]::Round($random_sec)
}
#Write-Host "Found unused" $rounded
}
$used_seconds += $rounded

# Outfile that screenshot will be saved as
$outfile = $outfolder + $infile + "_" + $rounded + ".png"

Write-Host $i/$how_many $outfile
#$result = & $ffmpeg -loglevel error -ss $rounded -i "$infile" -frames:v 1 $outfile | Out-String
#& $ffmpeg -loglevel error -ss $rounded -i "$infile" -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p -frames:v 1 $outfile

& $ffmpeg -loglevel error -ss $rounded -i "$infile" `
-vf "zscale=transfer=linear:npl=100,format=gbrpf32le,zscale=primaries=bt709,tonemap=tonemap=hable:desat=0,zscale=transfer=bt709:matrix=bt709:range=tv,format=yuv420p" `
-frames:v 1 $outfile

$i++
}
gorvic 
VIP


З нами з: 27.11.21
Востаннє: 29.06.25
Повідомлень: 891

2025-06-15 13:50  
4br4h4m написано:
HDR для...
Код:
tonemap=tonemap=hable:desat=0


Як я вже писав в одному каналі (із порівнянням результьатів), ось таке рекодування у SDR приводить до лайнових результатів. Потрібно використовувати інший метод, і тоді результат буде ідентичний оригіналу.
Код:
tonemap=tonemap=reinhard:desat=2


Додано через 19 хвилин 30 секунд:

І ще одне, використовувати такий метод для створення скріншотів - це звичайно збочення якесь, але робити по одному скріншоту в циклі - це взагалі треба бути ще тим нубом:

ось приблизно як це можна оптимізувати

Код:
# ігноруємо якийсь проміжок, щоб не скрінило заставку та початкові титри
-ss 00:03:00
# кажемо, що будемо робити скрін приблизно через кожну хвилину (або можна поставити своє)
-vf fps=0.01
# кількість скрінів, які треба зробити
-frames:v 8
# шлях та назва картинки. Де %03d буде замінено на порядковий номер
-y "screenshot_name_%03d.png"
gorvic 
VIP


З нами з: 27.11.21
Востаннє: 29.06.25
Повідомлень: 891

2025-06-28 01:26  
Я тут трішки почитав код для FFmpeg pngenc і побачив деякі приховані параметри, які можна використати

1. "-compression_level 0...9" (0-без стискання, 9 максимальна компрессія, по замовчування використовується 1)
2. попередній параметр має сенс тільки при використанні "-pred mixed"
3. бітність png файлу: rgb24=8біт, rgba=8біт+альфа канал, rgb48be=16біт, rgba64be=16біт+альфа канал (по замовчуванні)

все це напряму залежить від розміру вихідного скріншоту, то ж я б запропонував зробити десь такі батніки
make_sdr_screenshots.bat
Код:
@ECHO OFF

set scr_count=8
set compr_level=7

ffmpeg  -hide_banner -loglevel error -stats -i "%~1" -ss 00:03:00 -vf "fps=0.01,format=yuv420p" -pix_fmt:v rgb24 -pred mixed -compression_level %compr_level% -frames:v %scr_count% -y "%~dpn1_%%03d.png"
make_hdr_screenshots.bat
Код:
@ECHO OFF

set scr_count=8
set compr_level=7

ffmpeg  -hide_banner -loglevel error -stats -i "%~1" -ss 00:03:00 -vf "fps=0.01,zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=reinhard:desat=2,zscale=t=bt709:m=bt709:r=tv,format=yuv420p" -pix_fmt:v rgb24 -pred mixed -compression_level %compr_level% -frames:v %scr_count% -y "%~dpn1_%%03d.png"
і або кидати на них відразу файл фільму, або викликати з параметром

Код:
make_sdr_screenshots.bat "sdr_movie."
make_hdr_screenshots.bat "hdr_movie."


ps Можна зробити виклик всередені ще Info й самому вирішувати, HDR то, чи ні
Info
Код:

.......
set media_info=PathToMediaInfo
call :CHECK_HDR "%~1"

if %video_hdr%==false (
   ...
) else (
   ...
)
EXIT /b 0

:CHECK_HDR
set video_hdr=false
for /f "delims=" %%i in ('CALL %media_info% --Output^=Video^;^"%%HDR_Format/String%%\n^" "%~1"') do (
    for /f "tokens=1 delims=" %%k in ("%%i") do (
        if not "%%k"=="" (
            echo "%%k" | findstr "HDR" > nul 2>&1 && set video_hdr=true || set video_hdr=false
        )
    )
    EXIT /b 0
)
EXIT /b 0
Але то зайве, як для такого простого прикладу
gorvic 
VIP


З нами з: 27.11.21
Востаннє: 29.06.25
Повідомлень: 891

2025-06-29 00:41  
Вся ця ідея перетворилась у повноцінний скрипт, який використовує (якщо збірка ffmpeg дозволяє) метод трансформації, що задіяний у mpv player

https://gist.github.com/vikthedev/40417c75426670000c5e4bc5af2783e4
Ваш часовий пояс: GMT + 2 Години

Нова тема   Відповісти