큰 .mov 비디오 파일을 검은 색 프레임에서 작은 파일로 자동 분할합니까 (장면 변경)?


11

tape_01.mov, tape_02.mov, ..., tape_65.mov라는 65 개의 .mov 비디오 파일이 있습니다. 각각의 시간은 약 2.5 시간이며 많은 기가 바이트를 차지합니다. 그들은 VHS 테이프 (우리 가족의 "홈 무비")의 디지털 사본입니다.

각 2.5 시간 .mov 비디오 파일에는 많은 "챕터"가 포함됩니다 (때로는 장면이 검은 화면으로 희미 해졌다가 새 장면이 희미 해짐).

나의 주요 목표는 65 개의 큰 파일을 챕터별로 작은 파일로 분할하는 것입니다. 그리고 "장면"사이의 검은 색 프레임을 감지하여 자동으로 수행되기를 원합니다.

ffmpeg (또는 다른 것)를 사용하여 65 개의 원래 파일 이름을 전달하고 자동으로 각 비디오를 반복하고 잘라내어 결과 조각의 이름을 tape_01-ch_01.mov, tape_01-ch_02.mov, tape_01-ch_03.mov, 기타? 그리고 나는 다시 인코딩 하지 않고 그것을 원합니다 (간단한 무손실 슬라이스가되기를 원합니다).

어떻게해야합니까?

내가 원하는 단계는 다음과 같습니다.

  1. .mov 파일 (tape_01.mov, tape_02.mov, ..., tape_65.mov) 폴더를 mp4 파일 (tape_01.mp4, tape_02.mp4, ..., tape_65.mp4)로 내보내십시오. . 이 단계에서 압축 설정을 쉽게 구성하고 싶습니다. .mp4 파일을 만든 후에도 원본 .mov 파일이 계속 존재해야합니다.
  2. mp4 파일을 반복합니다. 각 mp4 파일에 대해 "장면"사이의 검은 색 세그먼트 시작 시간과 지속 시간을 지정하는 텍스트 파일을 생성하십시오. 각 mp4에는 이러한 검은 장면 나누기가 0 개 이상있을 수 있습니다. 시작 시간과 지속 시간은 1 초 단위로 정확해야합니다. HH : MM : SS 형식이 정확하지 않습니다.
  3. 그런 다음 텍스트 파일이 장면 변경 (검은 색 부분)을 올바르게 식별하는지 확인하기 위해 이러한 텍스트 파일을 수동으로 다시 확인할 수 있습니다. 원하는 경우 타이밍을 조정할 수 있습니다.
  4. 또 다른 스크립트는 각 mp4 파일과 해당 txt 파일을 읽고 텍스트 파일의 행 (여기에 표시된 타이밍)을 기준으로 mp4를 필요한만큼 많은 조각으로 분할합니다 (초고속 무손실 방식으로). 분할은 각 검은 색 세그먼트의 중간에 발생해야합니다. 다음은 검은 색 장면이 바뀐 샘플 mp4 파일 (개인 정보 보호 목적으로 오디오가 제거 된 상태)입니다. 작은 mp4 파일이 만들어 질 때 원본 mp4 파일은 그대로 유지됩니다. 작은 mp4 파일은 이름이 tape_01_001.mp4, tape_01_002.mp4, tape_01_003.mp4 등이어야합니다.
  5. 보너스! 다른 스크립트가 작은 mp4 파일을 반복 하고이 사람이 시도하는 것과 같은 스크린 샷의 모자이크 인 각각에 대해 하나의 .png 이미지를 생성 할 수 있다면 좋을 것입니다 : FFmpeg를 사용하는 비디오의 의미있는 썸네일

이 스크립트를 Mac, Ubuntu 및 Windows Git Bash에서 실행할 수있는 셸 스크립트로 사용하고 싶습니다.

답변:


11

긴 비디오를 검은 색 장면으로 작은 장으로 나누는 두 가지 PowerShell 스크립트는 다음과 같습니다.

Detect_black.ps1 및 Cut_black.ps1로 저장하십시오. Windows 용 ffmpeg를 다운로드 하고 옵션 섹션에서 ffmpeg.exe 및 비디오 폴더의 경로를 스크립트에 알려주십시오.

여기에 이미지 설명을 입력하십시오

두 스크립트 모두 기존 비디오 파일을 건드리지 않고 그대로 유지됩니다.
그러나 입력 비디오가있는 곳에서 몇 개의 새로운 파일을 얻게됩니다.

  • 사용 된 ffmpeg 명령 모두에 대한 콘솔 출력이있는 비디오 당 로그 파일
  • 수동 미세 조정을위한 모든 검은 색 장면의 타임 스탬프가있는 비디오 당 CSV 파일
  • 이전에 감지 된 검은 장면 수에 따른 몇 가지 새로운 비디오

여기에 이미지 설명을 입력하십시오


실행할 첫 스크립트 : Detect_black.ps1

 ### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"            # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"              # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4")        # Set which file extensions should be processed
$dur = 4                            # Set the minimum detected black duration (in seconds)
$pic = 0.98                         # Set the threshold for considering a picture as "black" (in percent)
$pix = 0.15                         # Set the threshold for considering a pixel "black" (in luminance)

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### analyse each video with ffmpeg and search for black scenes
  & $ffmpeg -i $video -vf blackdetect=d=`"$dur`":pic_th=`"$pic`":pix_th=`"$pix`" -an -f null - 2> $logfile

  ### Use regex to extract timings from logfile
  $report = @()
  Select-String 'black_start:.*black_end:' $logfile | % { 
    $black          = "" | Select  start, end, cut

    # extract start time of black scene
    $start_s     = $_.line -match '(?<=black_start:)\S*(?= black_end:)'    | % {$matches[0]}
    $start_ts    = [timespan]::fromseconds($start_s)
    $black.start = "{0:HH:mm:ss.fff}" -f ([datetime]$start_ts.Ticks)

    # extract duration of black scene
    $end_s       = $_.line -match '(?<=black_end:)\S*(?= black_duration:)' | % {$matches[0]}
    $end_ts      = [timespan]::fromseconds($end_s)
    $black.end   = "{0:HH:mm:ss.fff}" -f ([datetime]$end_ts.Ticks)    

    # calculate cut point: black start time + black duration / 2
    $cut_s       = ([double]$start_s + [double]$end_s) / 2
    $cut_ts      = [timespan]::fromseconds($cut_s)
    $black.cut   = "{0:HH:mm:ss.fff}" -f ([datetime]$cut_ts.Ticks)

    $report += $black
  }

  ### Write start time, duration and the cut point for each black scene to a seperate CSV
  $report | Export-Csv -path "$($video.FullName)_cutpoints.csv" –NoTypeInformation
}

작동 원리

첫 번째 스크립트는 지정된 확장명과 일치하고 패턴과 일치하지 않는 모든 비디오 파일을 반복합니다. *_???.*새 비디오 챕터의 이름이 지정 <filename>_###.<ext>되었으므로 제외하려고합니다.

모든 검은 색 장면을 검색하고 시작 타임 스탬프 및 검은 색 장면 기간을 새 CSV 파일에 기록 <video_name>_cutpoints.txt

또한 다음과 같이 절단 점을 계산 cutpoint = black_start + black_duration / 2합니다. 나중에이 타임 스탬프에서 비디오가 분할됩니다.

샘플 비디오 의 cutpoints.txt 파일은 다음과 같습니다.

start          end            cut
00:03:56.908   00:04:02.247   00:03:59.578
00:08:02.525   00:08:10.233   00:08:06.379

실행 후 원하는 경우 컷 포인트를 수동으로 조작 할 수 있습니다. 스크립트를 다시 실행하면 이전의 모든 내용이 덮어 쓰기됩니다. 작업을 수동으로 편집하고 다른 곳에 저장할 때는주의하십시오.

샘플 비디오의 경우 검은 장면을 감지하는 ffmpeg 명령은

$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null

스크립트의 옵션 섹션에서 편집 할 수있는 3 개의 중요한 숫자가 있습니다

  • d=4 4 초 이상의 검은 장면 만 감지됨을 의미
  • pic_th=0.98 사진을 "검은 색"(백분율)으로 간주하기위한 임계 값
  • pix=0.15픽셀을 "검정"(휘도)으로 간주하기위한 임계 값을 설정합니다. 오래된 VHS 비디오가 있으므로 비디오에 완전히 검은 장면이 없습니다. 기본값 10이 작동하지 않고 임계 값을 약간 늘려야했습니다.

문제가 발생하면이라는 해당 로그 파일을 확인하십시오 <video_name>__ffmpeg.log. 다음 줄이 없으면 검은 색 장면이 모두 감지 될 때까지 위에서 언급 한 숫자를 늘리십시오.

[blackdetect @ 0286ec80]
 black_start:236.908 black_end:242.247 black_duration:5.33877



실행할 두 번째 스크립트 : cut_black.ps1

### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"            # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"              # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4")        # Set which file extensions should be processed

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."  
  $cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","

  ### put together the correct new name, "%03d" is a generic number placeholder for ffmpeg
  $output = $video.directory.Fullname + "\" + $video.basename + "_%03d" + $video.extension

  ### use ffmpeg to split current video in parts according to their cut points
  & $ffmpeg -i $video -f segment -segment_times $cuts -c copy -map 0 $output 2> $logfile        
}

작동 원리

두 번째 스크립트는 첫 번째 스크립트와 동일한 방식으로 모든 비디오 파일을 반복합니다. 그것은 단지 읽 대응에서 타임 스탬프 cutpoints.txt비디오의.

다음으로, 챕터 파일에 적합한 파일 이름을 만들고 ffmpeg에게 비디오를 분할하도록 지시합니다. 현재 비디오는 다시 인코딩하지 않고 슬라이스됩니다 (초고속 및 무손실). 이로 인해 ffmpeg는 key_frames에서만 잘라낼 수 있으므로 컷 포인트 타임 스탬프와 함께 1-2 초의 부정확성이있을 수 있습니다. 우리는 단지 복사하고 다시 인코딩하지 않기 때문에 우리 자신의 key_frames를 삽입 할 수 없습니다.

샘플 비디오의 명령은

$ffmpeg -i "Tape_10_3b.mp4" -f segment -segment_times "00:03:59.578,00:08:06.379" -c copy -map 0 "Tape_10_3b_(%03d).mp4"

문제가 발생하면 해당 ffmpeg.log를 살펴보십시오.



참고 문헌



할 것

  • 텍스트 형식보다 CSV 형식이 컷 포인트 파일보다 좋은지 OP에게 문의하면 Excel로 쉽게 편집 할 수 있습니다
    »구현
  • 타임 스탬프 형식을 초 단위가 아닌 [hh] : [mm] : [ss], [밀리 초]로 형식화하는 방법
    구현»구현
  • 각 장마다 mosaik png 파일을 작성하는 ffmpeg 명령
    구현»구현
  • -c copyOP의 시나리오에 충분한 지 또는 우리가 완전히 다시 인코딩해야 하는지를 설명 합니다.
    Ryan이 이미있는 것 같습니다 .

얼마나 철저한 대답입니까! 감사합니다! 불행히도, 나는 지금 맥에 있고 이것을 Bash로 번역하는 방법을 먼저 알아야합니다.
Ryan

PowerShell 에서이 기능을 전혀 사용할 수 없었습니다. 로그 파일은 비어 있습니다.
Ryan

도움이되도록 업로드 할 수있는 내용을 볼 수 있습니다. 계속 지켜봐 주시기 바랍니다. 관심을 가져 주셔서 감사합니다!
Ryan

질문에 샘플 mp4를 추가했습니다. 당신의 도움을 주셔서 감사합니다!
Ryan

개인 정보 보호를 위해 실제 소스 MOV를 업로드하지 않습니다. 나는 그것을 잘라 내고 (mp4에서했던 것처럼) 오디오를 제거하고 싶습니다. 따라서 어쨌든 진정한 원본은 아닙니다. 그럼에도 불구하고 업로드하기에는 너무 큽니다. 그리고 장면 분할 단계는 아마도 디스크 공간 목적으로 MOV 파일 대신 mp4 파일에서 발생해야합니다. 감사!
Ryan

3

보너스는 다음과 같습니다.이 3 번째 PowerShell 스크립트는 모자이크 축소판 이미지를 생성합니다.

챕터 비디오 당 하나의 이미지를 받게되며 아래 이미지와 같습니다.

### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe"       # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*"         # Set path to your video folder; '\*' must be appended
$x = 5                         # Set how many images per x-axis
$y = 4                         # Set how many images per y-axis
$w = 384                       # Set thumbnail width in px (full image width: 384px x 5 = 1920px,)

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include "*_???.mp4" -r){

  ### get video length in frames, an ingenious method
  $log = & $ffmpeg -i $video -vcodec copy -an -f null $video 2>&1   
  $frames = $log | Select-String '(?<=frame=.*)\S+(?=.*fps=)' | % { $_.Matches } | % { $_.Value }  
  $frame = [Math]::floor($frames / ($x * $y))

  ### put together the correct new picture name
  $output = $video.directory.Fullname + "\" + $video.basename + ".jpg"

  ### use ffmpeg to create one mosaic png per video file
  ### Basic explanation for -vf options:  http://trac.ffmpeg.org/wiki/FilteringGuide
#1  & $ffmpeg -y -i $video -vf "select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#2  & $ffmpeg -y -i $video -vf "yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output 
    & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output       

#4  & $ffmpeg -y -i $video -vf "select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#5  & $ffmpeg -y -i $video -vf "yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#6  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  

#7  & $ffmpeg -y -i $video -vf "thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#8  & $ffmpeg -y -i $video -vf "yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#9  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
}

주요 아이디어는 전체 비디오에 걸쳐 연속적인 이미지 스트림을 얻는 것입니다. 우리는 ffmpeg의 select 옵션을 사용합니다.

먼저, 독창적 인 방법 (예 : 2000)으로 전체 프레임 수를 검색하여 기본 섬네일 수 (예 : 5 x 4 = 20)로 나눕니다. 그래서 우리는 100 프레임마다 하나의 이미지를 생성하고 싶습니다2000 / 20 = 100

축소판을 생성하는 결과 ffmpeg 명령은 다음과 같습니다.

ffmpeg -y -i input.mp4 -vf "mpdecimate,yadif,select=not(mod(n\,100)),scale=384:-1,tile=5x4" -frames:v 1 output.png

위 코드에서 9 가지 -vf조합 으로 구성되어 있습니다.

  • select=not(mod(n\,XXX)) 여기서 XXX는 계산 된 프레임 속도입니다.
  • thumbnail 가장 대표적인 프레임을 자동으로 선택하는
  • select='gt(scene\,XXX)+ -vsync vfr여기서 XXX는 재생해야하는 임계 값입니다.
  • mpdecimate-거의 중복 된 프레임을 제거합니다. 검은 장면에 적합
  • yadif-입력 이미지를 디인터레이스하십시오. 이유를 모르지만 작동합니다.

제 생각에는 버전 3이 최선의 선택입니다. 다른 모든 사람들은 주석 처리되었지만 여전히 시도해 볼 수 있습니다. mpdecimate, yadif및을 사용하여 모호한 축소판 그림을 대부분 제거 할 수있었습니다 select=not(mod(n\,XXX)). 네!

당신을 위해 샘플 비디오 나는이 미리보기를 얻을

여기에 이미지 설명을 입력하십시오
클릭하면 확대

여기에 이미지 설명을 입력하십시오
클릭하면 확대

해당 버전으로 만든 모든 미리보기 이미지를 올렸 습니다. 전체 비교를 위해 그것들을 살펴보십시오.


아주 멋지다! 이것 좀 살펴 볼게요. 오늘도 select='gt(scene\,0.4)'일하러 가려고 몇 시간을 낭비 했습니다. 그리고 나는 fps="fps=1/600"일도 할 수 없었다 . 그건 그렇고, superuser.com/questions/725734에 대한 아이디어가 있습니까? 모든 도움을 주셔서 감사합니다. 나는 가족이 많은 오래된 기억을 발견하도록 돕는 것에 매우 흥분합니다.
Ryan

맨 아래에 나열된 5 가지 다른 인코딩 변형 을 사용해 보셨습니까 ? "장면"옵션에 대한 실제 예제를 추가했습니다. FPS 필터는 잊어 버리십시오. 나는 대부분의 모호한 엄지 손가락을 제거했다 :)
nixda

0

비디오를 분할하는 좋은 방법 인 두 스크립트를 모두 감사하십시오.

그래도 나중에 올바른 시간을 표시하지 않는 비디오에 문제가있어 특정 시간으로 이동할 수 없었습니다. 한 가지 해결책은 Mp4Box를 사용하여 스트림을 디 먹싱 한 다음 멀티플렉싱하는 것입니다.

나에게 더 쉬운 또 다른 방법은 mkvmerge를 사용하여 분할하는 것입니다. 따라서 두 스크립트를 모두 변경해야했습니다. detect_black.ps1의 경우 "* .mkv"만 필터 옵션에 추가해야했지만 mp4 파일로만 시작하는 것은 아닙니다.

### Options        
$mkvmerge = ".\mkvmerge.exe"        # Set path to mkvmerge.exe
$folder = ".\*"                     # Set path to your video folder; '\*' must be appended
$filter = @("*.mov", "*.mp4", "*.mkv")        # Set which file extensions should be processed

### Main Program 
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){

  ### Set path to logfile
  $logfile = "$($video.FullName)_ffmpeg.log"

  ### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."  
  $cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","

  ### put together the correct new name, "%03d" is a generic number placeholder for mkvmerge
  $output = $video.directory.Fullname + "\" + $video.basename + "-%03d" + ".mkv"

  ### use mkvmerge to split current video in parts according to their cut points and convert to mkv
  & $mkvmerge -o $output --split timecodes:$cuts $video
}

기능은 동일하지만 지금까지 비디오 시간에는 문제가 없습니다. 영감을 주셔서 감사합니다.


-2

나는 내가 틀렸기를 바란다. 그러나 나는 당신의 요구가 무엇인지 생각하지 않는다. 비디오를 다시 인코딩하는 동안 가능할 수도 있지만 "간단한 무손실 슬라이스"로서 나는 어떤 방법도 모릅니다.

많은 비디오 편집 프로그램에는 자동 분석 기능이나 비디오를 검은 색 프레임으로 분할 할 수있는 동등한 기능이 있지만 비디오를 다시 인코딩해야합니다.

행운을 빕니다!


절대적으로 가능합니다. 문제없이 비디오를 무손실 방식으로 슬라이스 할 수 있습니다 (예 : ffmpeg의 세그먼트 멀티플렉서의 경우 비디오 자르기의 ffmpeg 위키 참조 ). 괜찮은 비디오 편집 프로그램은 가능합니다. 유일한 문제는 장면 변경을 감지하는 것입니다.
slhck

1
실제로는 할 수 없습니다. 가장 큰 문제는 키 프레임 일 수 있습니다. 편집 도구에서도 재생할 때 올바르게 렌더링하려면 모든 클립을 키 프레임으로 시작해야합니다. 검은 색 프레임이 키 프레임으로 시작하지 않으면 무손실 컷을 사용하는 동안 비 호환성이 발생합니다.
JasonXA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.