Что нового

Извлечение кадров из видеоролика

Grell

Новичок
Сообщения
127
Репутация
0
Здравствуйте.
Подскажите - как извлечь из видеоролика 1.mp4 - кадры (в формате jpg, диапазоном 60сек, с размером 300 пикселей в ширину, с нумерацией 001,002...003) ?

Или даже не из 1.mp4 - а вообще из любого видеоформата.

Пока что я использую вот такой скрипт:
Код:
Opt('WinTitleMatchMode', -2)

WinWaitActive('1.mp4')
For $i=1 to 60
Send("!ш")
Sleep(500)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(500)
Send("{ENTER}")
Sleep(500)
;Send("{RIGHT}")
Next

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

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
С помощью DSEngine_UDF.dll
Она и все нужные файлы для запуска есть в этом сообщении(файлы положить в папку с примером):
http://autoit-script.ru/index.php?topic=2949.msg141535#msg141535

Вот быстренько накидал код на основе какого-то моего старого плеера:
Код:
#NoTrayIcon
#include <GDIPlus.au3>
#include <DSEngine.au3>
#include <Constants.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>

;~ If Not FileExists(@ScriptDir & '\DSEngine_UDF.dll') Then
;~ 	MsgBox(262144, 'Ошибка', 'DSEngine_UDF.dll отсутствует!')
;~ 	Exit
;~ EndIf

Global $sTime = ''
Global $z = 0
Global $n = 0
Global $n1 = 0
Global $hTimer
Global $iperiod = 60000 ; период между скриншотами в миллисекундах


$hForm = GUICreate('1GUI', 700, 500)
$h_cGUI1 = GUICreate('Управление', 700, 50, -1, -1, -1, -1, $hForm)

$Button1 = GUICtrlCreateButton('Play', 4, 8, 75, 25, 0)
$Button2 = GUICtrlCreateButton('Stop', 93, 8, 75, 25, 0)
$Button3 = GUICtrlCreateButton(' ... ', 600, 8, 71, 25, 0)
$Label1 = GUICtrlCreateLabel('0.00', 188, 8, 120, 24)
GUICtrlSetFont(-1, 13, 400, 0, 'MS Sans Serif')
GUICtrlSetBkColor(-1, 0x000000)
GUICtrlSetColor(-1, 0x00FF00)
_GDIPlus_Startup()

$Input2 = GUICtrlCreateInput('', 332, 8, 265, 24)
GUIRegisterMsg($WM_MOVE, 'WM_MOVE')
WM_MOVE($hForm, $WM_MOVE, 0, 0)
GUISetState(@SW_SHOW)

Engine_Startup(@ScriptDir & '\DSEngine_UDF.dll')


While 1

	If Engine_GetState() <> 0 Then

		If $sTime <> Round(Engine_GetPosition(), 2) Then
			GUICtrlSetData($Label1, Round(Engine_GetPosition(), 2))
			$n += 1
		Endif

		$iDiff = TimerDiff($hTimer)

		If $iDiff > $iperiod Then
			$n1 += 1
			ConsoleWrite(StringFormat('%04d', $n1) & @CRLF)
			$hBMP = Engine_GetCurrentFrame() ; получение картинки
			$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
			$sCLSID = _GDIPlus_EncodersGetCLSID("JPG")
			_GDIPlus_ImageSaveToFileEx($hImage, @ScriptDir & "\" & StringFormat('%04d', $n1) & ".jpg", $sCLSID)
			_GDIPlus_ImageDispose($hImage)
			_WinAPI_DeleteObject($hBMP)
			; картинка получается перевернутая или зеркальная, вообщем, нужно еще переделывать
			$hTimer = TimerInit()
		Endif

	Endif

	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Engine_Shutdown()
			Exit
		Case $Button1
			If $z = 0 Then
				Engine_LoadFile(GUICtrlRead($Input2), $hForm)
				$aSize = Engine_GetVideoSize()
				WinMove($hForm, '', Default, Default, $aSize[0], $aSize[1])
				WinSetTitle($hForm, '', GUICtrlRead($Input2))
				GUISetState(@SW_SHOW, $hForm)
				Engine_StartPlayback()
				$z = 1
			Else
				Engine_StartPlayback()
			EndIf
		Case $Button2
			Engine_PausePlayback()
		Case $Button3
			$video = FileOpenDialog('', @ScriptDir & '\', 'Файлы (*.avi;*.mpg;*.mp4;*.wmv)', 1)
			If @error = 0 Then
				GUICtrlSetData($Input2, $video)
				$z = 0
			EndIf
	EndSwitch
	Sleep(10)
WEnd

_GDIPlus_Shutdown()
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hForm
			Local $Pos = WinGetPos($hForm)
			If IsArray($Pos) Then
				WinMove('[TITLE:Управление; CLASS:AutoIt v3 GUI]', '', $Pos[0], $Pos[1] + 520)
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

Картинки получаются то ли перевернутые, то ли зеркальные
И размер, конечно, как у видео
Картинки сохраняются в папку со скриптом
На форуме есть примеры для изменения картинки
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
WSWR , так это же по функциональности - такой же скрипт как и у меня.
Между скриншотами, которые скрипт делает - приходится ждать 60 секунд.

Он не проматывает эти промежутки.
В результате - чтобы полностью получить скриншоты видеофайла (с диапазоном в 60сек) - нужно ждать столько же, сколько видеофайл длится.
Если видеофайл длится 2 часа, значит скрипт - будет извлекать скриншоты 2 часа.

Я-то имел ввиду быструю промотку, с созданием скриншотов через заданные диапазоны.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
он не прокручивает время ожидания срабатывания до следующего скриншота
Если вы автоматизируете MPC, то там есть "Навигация - Переход... Ctrl+G"
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI Да, есть такое.
Если нажать на Ctrl+G - вылетает окошко, где можно указать к какому времени видеофайла перейти (или к какому кадру перейти).

А как это в скрипте реализовать ?
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
Grell сказал(а):
WSWR , так это же по функциональности - такой же скрипт как и у меня.
Между скриншотами, которые скрипт делает - приходится ждать 60 секунд.

Он не проматывает эти промежутки.
В результате - чтобы полностью получить скриншоты видеофайла (с диапазоном в 60сек) - нужно ждать столько же, сколько видеофайл длится.
Если видеофайл длится 2 часа, значит скрипт - будет извлекать скриншоты 2 часа.

Я-то имел ввиду быструю промотку, с созданием скриншотов через заданные диапазоны.

В чем проблема изменить код как нужно?


Код:
#NoTrayIcon
#include <GDIPlus.au3>
#include <DSEngine.au3>
#include <Constants.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>

;~ If Not FileExists(@ScriptDir & '\DSEngine_UDF.dll') Then
;~ 	MsgBox(262144, 'Ошибка', 'DSEngine_UDF.dll отсутствует!')
;~ 	Exit
;~ EndIf

Global $n1 = 0
Global $dTime = -1
Global $iperiod = 60 ; период между скриншотами в секундах

$hForm = GUICreate('1GUI', 300, 300)
_GDIPlus_Startup()

Engine_Startup(@ScriptDir & '\DSEngine_UDF.dll')


$video = FileOpenDialog('', @ScriptDir & '\', 'Файлы (*.avi;*.mpg;*.mp4;*.wmv)', 1)

If @error = 0 Then
	Engine_LoadFile($video, $hForm)
	Engine_StartPlayback()
Else
	_GDIPlus_Shutdown()
	Engine_Shutdown()
	Exit
EndIf


While 1
	If Engine_GetState() <> 0 Then
		If $dTime <= Engine_GetLength() Then
			$n1 += 1
			ConsoleWrite(StringFormat('%04d', $n1) & @CRLF)
			$hBitmap = Engine_GetCurrentFrame() ; получение картинки

			$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
			_WinAPI_DeleteObject($hBitmap)
			_GDIPlus_ImageRotateFlip($hImage, 6) ; поворот на 180 градусов  зеркальное отражение


			$hThumb = _GDIPlus_GetImageThumbnail($hImage, 300, 300) ; установка размера картинки

			_GDIPlus_ImageDispose($hImage)
			_GDIPlus_ImageSaveToFile($hThumb, @ScriptDir & "\" & StringFormat('%04d', $n1) & ".jpg")
			_GDIPlus_ImageDispose($hThumb)
			$dTime = $n1 * $iperiod
			Engine_SetPosition($dTime)
		Else
			Exit
		Endif
	Endif
	Sleep(10)
WEnd

_GDIPlus_Shutdown()
Engine_Shutdown()

Func _GDIPlus_GetImageThumbnail($hImage, $iWidth, $iHeight)

	Local $Ret = DllCall("gdiplus.dll", 'int', 'GdipGetImageThumbnail', 'ptr', $hImage, 'int', $iWidth, 'int', $iHeight, 'ptr*', 0, 'ptr', 0, 'ptr', 0)

	If(@error) Or($Ret[0]) Then
		Return SetError(1, 0, 0)
	EndIf
	Return $Ret[4]
EndFunc   ;==>_GDIPlus_GetImageThumbnail
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
как это в скрипте реализовать
Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
  $i += 1
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ; код скриншота
  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
Вот я сейчас использую вот такой код:

Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
 ; For $i += 1
  For $i=1 to 60
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  ;If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ; код скриншота

Send("!ш")
Sleep(100)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(100)
Send("{ENTER}")
Sleep(100)


 Next
 If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")

Но не понятно - где изменять продолжительность пропуска между скриншотами.
Какой параметр в коде - за это отвечает ?
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
где изменять продолжительность пропуска между скриншотами
В моём коде - здесь
Код:
$str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
$i - текущая минута, 60 - количество секунд в минуте, $arr[2] - количество кадров в секунду.
Другими словами: $i - счётчик, 60 - интервал в секундах.
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI, ясно.
А вот эту строчку - в моем последнем коде - куда добавлять ?
Код:
If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
эту строчку - в моем последнем коде - куда добавлять ?
Если бы я понял смысл добавления цикла For, возможно, я бы ответил на этот вопрос... :scratch:
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
Тогда вот такой код, без цикла For :
Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
 ;
 $i += 1
 ;  For $i=1 to 60
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  ;If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ; код скриншота

Send("!ш")
Sleep(100)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(100)
Send("{ENTER}")
Sleep(100)


 ;Next
 If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop


  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")
Почему не срабатывает строчка ?
Код:
If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
Почему не срабатывает строчка ?
Строка не срабатывает, потому что вы её перенесли.
Окно с сообщением о превышении времени появляется СРАЗУ после нажатия Button2. Если оно появилось, то скриншот уже не сделать, т.к. вы не дождётесь окна 'Сохранить как'.

Неужели так сложно просто добавить код скриншота туда, где это указано?
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
Поставил эту строчку - как в вашем коде.
Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
 ;
 $i += 1
 ;  For $i=1 to 60
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ; код скриншота

Send("!ш")
Sleep(100)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(100)
Send("{ENTER}")
Sleep(100)


 ;Next



  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")

Все равно не работает.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
Все равно не работает
Что конкретно не работает? Время не перематывает? Окно с сообщением о превышении времени не появляется? Скриншот не создаётся?
Я проверил ваш последний код на MPC-HC версии 1.7.13 и получил 4 снимка на клипе длительностью 04:18.
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
Если поставить эту строчку - вниз (как у меня) - то скрипт будет создавать скриншоты. Но потом - не уберет открытое окно ("Введенное время больше длительности файла") и не прекратит свою работу. Будет продолжать висеть в трее.
Если поставить эту строчку как у вас (то есть в центре) - то скрипт не будет создавать скриншоты, а будет только перематывать видеофайл.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell [?]
Если поставить эту строчку как у вас (то есть в центре) - то скрипт не будет создавать скриншоты, а будет только перематывать видеофайл
У меня только одно объяснение: вы запускаете не тот скрипт, который выложили в предыдущем своём сообщении (#13).
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
Вот этот скрипт , где строчка If WinExists("MPC-HC" - стоит в середине а (как у вас) - Не делает скриншотов, а просто проматывает видеофайл.
Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
 ;
 $i += 1
 ;  For $i=1 to 60
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ;  скриншота

Send("!ш")
Sleep(100)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(100)
Send("{ENTER}")
Sleep(100)


 ;Next



  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")

А вот этот скрипт, где строчка If WinExists("MPC-HC" - стоит в конце а - делает скриншоты, но не закрывает окна "Введенное время больше длительности файла." в самом конце. И не закрывает скрипт в трее.

Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
 ; For $i += 1
  For $i=1 to 60
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  ;If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop
  ; -------------
  ;  скриншота

Send("!ш")
Sleep(100)
WinWaitActive('Сохранить как')
Send($i&'.png')
Sleep(100)
Send("{ENTER}")
Sleep(100)


 Next
 If WinExists("MPC-HC", "Введенное время больше длительности файла.") Then ExitLoop


  ; -------------
WEnd
WinClose("MPC-HC", "Введенное время больше длительности файла.")
WinClose("Переход...")
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Grell
Хорошо. Перепишем по другому
Код:
WinActivate("[class:MediaPlayerClassicW]")

$i = 0
While 1
  $i += 1
  WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Навигация", "Переход...")
  WinWaitActive("Переход...")
  $arr = StringSplit(ControlGetText("Переход...", "", "Edit1"), ", ", 1)
  $str = Round($i * 60 * $arr[2]) & ", " & $arr[2]
  ControlSetText("Переход...", "", "Edit1", $str)
  ControlClick("Переход...", "", "Button2")
  Sleep(111)
  If Not WinExists("MPC-HC", "Введенное время больше длительности файла") Then
    WinMenuSelectItem("[class:MediaPlayerClassicW]", "", "Файл", "Сохранить изображение...")
    WinWaitActive("Сохранить как")
    Send($i & ".png")
    Send("{ENTER}")
    WinWaitClose("Сохранить как")
  Else
    WinClose("MPC-HC", "Введенное время больше длительности файла")
    WinClose("Переход...")
    ExitLoop
  EndIf
WEnd
 
Автор
G

Grell

Новичок
Сообщения
127
Репутация
0
InnI
А сейчас все идеально.
Огромное спасибо.
 
Верх