Log in

View Full Version : I show you my script for detecting scene change in a video file; using mvtools2


Jeremy Duncan
1st May 2011, 07:51
Link to zip files with avs files and the 2 dlls you need to run this script (http://www.mediafire.com/?9kd0diiuh884m6u)

Here's my script I used to tweak the settings. it makes a side by side video I can run in realtime in megui:


loadplugin("C:\Program Files\AviSynth 2.5\plugins\DirectShowSource.dll")
loadplugin("C:\Program Files\AviSynth 2.5\plugins\mvtools2.dll")
source = directshowsource("C:\Users\Donkey\Downloads\121819956.vc1", audio=false, 23.976)
source_2 = directshowsource("C:\Users\Donkey\Downloads\121819956.vc1", audio=false, 23.976)
vectors = source.MSuper(pel=2, hpad=12, vpad=12, rfilter=2, isse=true).MAnalyse(isb=false, search=3, searchparam=3, isse=true, lambda=440, dct=4)
source.MSCDetection(vectors, thscd1=248, thscd2=50)
return stackhorizontal(last, source_2).converttoyv12()


And here's the scene change script. What it does is it shows either a green screen or a pink screen;
The pink screen flashing means the scene changed -
The green screen means the scene has not changed.


loadplugin("C:\Program Files\AviSynth 2.5\plugins\DirectShowSource.dll")
loadplugin("C:\Program Files\AviSynth 2.5\plugins\mvtools2.dll")
source = directshowsource("C:\Users\Donkey\Downloads\121819956.vc1", audio=false, 23.976)
vectors = source.MSuper(pel=2, hpad=12, vpad=12, rfilter=2, isse=true).MAnalyse(isb=false, search=3, searchparam=3, isse=true, lambda=440, dct=4)
source.MSCDetection(vectors, thscd1=248, thscd2=50)
converttoyv12()


I want to use writefile so the scene change can be logged.
And I want the log to be a list if trim; each scene is matched up to it's trim.
I wonder if I can ask somebody here - to add code so I can do this?

:script: :)

Edit, What parameter to tweak so the pink flash matches the video source better when tweaking the source side by side: lambda. Adjust lambda and see the pink screen flash either better or worse, even a difference of 10 will change the results for the better or worse in some cases.

Gavino
1st May 2011, 10:21
I want to use writefile so the scene change can be logged.
And I want the log to be a list if trim; each scene is matched up to it's trim.
See the script posted by frustum, and my following corrections (ignore my WriteFileEnd suggestion, which was wrong). Also later comments from johnmeyer.

You must have seen this, as you took part in the following discussion.

TheRyuu
1st May 2011, 10:34
The following (probably ugly) perl code takes an xvid first pass file as its only argument (either generated by doing a 1st pass yourself or via scxvid, if you do it yourself make sure to set keyint to something huge (500000)) and spits out an avs file with just trims.

I have no idea how it compares to scene change detection by mvtools (if I had to guess xvid is probably better but I honestly have no idea). Feel free to modify the print statements to how you want your avs file to look.

Edit: New script here (http://forum.doom9.org/showthread.php?p=1497698#post1497698).

Additional stuff in the above script Gavino linked too like minimum scene size could be easily added.

Jeremy Duncan
1st May 2011, 23:17
I made this. It is flawed in the following ways though:
1.) The pink screens aren't being tagged with trim; the trim isn't matching the pink screens.
2.) The txt file will show trim while encoding, but after encoding the text file is empty.
3.) I'm not sure the code is effective. I got some code from scharfis brain on how to detect scene change, but I'm not sure I implemented it properly:

Scharfis brain wrote:
"scene changes can be detected in an automated way using AVIsynth:

- create a motionmask
- calculate the averageluma x()
- if [x(c) > x(p) * fac + off] AND [x(c) > x(n) * fac + off] then scenechange else noscenechange

p - previous frame
c - current frame
n - next frame
fac - multiplier (5 is good) ; defines the luma distance
off - offset ; (try) ; helping to ignore noise"
But I couldn't find the words "next frame in my script, so I compromised with "prev_scene_start"

Here is my script. I'm going out for a while and thought I would post this in the mean while:


bad code removed


:script: :), :helpful:

Didée
2nd May 2011, 00:20
Scharfis brain wrote:
"scene changes can be detected in an automated way using AVIsynth:

- create a motionmask
- calculate the averageluma x()
- if [x(c) > x(p) * fac + off] AND [x(c) > x(n) * fac + off] then scenechange else noscenechange

The harder way is trying to implement this. (Okay, it's not THAT hard, but still....)

The easier way is to load RemoveDirt.dll, and simply use the SCSelect() filter. ;)

Jeremy Duncan
2nd May 2011, 03:29
I got it working, here is the ode:


bad code removed


here is my text file with the list of trim:


bad code removed


As you can see, the top and bottom of the lis is cluttered with "falsescene1".

I tried to get scselect to work but I couldn't get it to go.
Could you write how to do that?

:helpful: :goodpost:


:sly:

Gavino
2nd May 2011, 12:11
global global_motion = 5
...
FrameEvaluate(last, """
# detect new scene
global newscene = (current_frame < scene_end + global_motion) ? false \
...
""")
...
# log scene changes at end of film
WriteFileEnd(file_out, "current_frame==framecount-1", \
""""scene"""", "scene_num", ... )

As you can see, the top and bottom of the lis is cluttered with "falsescene1".
That's because WriteFileEnd should be WriteFileIf, as in frustum's code. (I said above not to use WriteFileEnd.)
And by removing the original WriteFileStart code, you are appending to the file on each run instead of starting a new file.

Also, why have you renamed frustum's variable 'min_scene_size' to 'global_motion' when it has nothing to do with motion?

Jeremy Duncan
3rd May 2011, 01:09
Here is my code and text file list:


bad code removed


If I include the writefilestart code the text file is empty after the encoding finishes.

May I ask if it's possible to encode once and have multiple output clips from a source file? If so I would like to run the trim list to do this.

TheRyuu
3rd May 2011, 08:07
In case anyone cares, modified my script to more of a format to what the other one was producing.

Still takes a xvid first pass file as it's only argument, also added a min-scene size variable.

#!/usr/bin/env perl

use warnings;
use strict;

my $xvidpass = shift;
open(PASSFILE, "<", $xvidpass) || die("Cannot open $xvidpass");
open(OUTPUT, ">", $xvidpass.".avs") || die("Cannot open new output file");

my ($currentframe, $startframe, $endframe, $minsize, $scenenumber) = (0, 0, 0, 5, 1);
<PASSFILE>;<PASSFILE>;<PASSFILE>; #skip first 3 lines

while(<PASSFILE>) {
if ($_ =~ /i/) {
if (($currentframe - $startframe) > $minsize) {
$endframe = $currentframe - 1;
print OUTPUT "scene$scenenumber = trim($startframe,$endframe)\r\n";
$startframe = $currentframe;
$scenenumber++;
}
}
$currentframe++;
}

close PASSFILE;
$endframe = $currentframe - 1;
print OUTPUT "scene$scenenumber = trim($startframe,$endframe)\r\n";

for (my $i = 1; $i < $scenenumber; $i++) {
print OUTPUT "scene$i++"
}
print OUTPUT "scene$scenenumber";
close OUTPUT;

Gavino
3rd May 2011, 10:10
Here is my code and text file list:
...
vectors = source.MSuper(pel=2, hpad=12, vpad=12, rfilter=2, isse=true).MAnalyse(isb=true, ...)
...
...
scene_27 = trim(1075,1150)
scene_28 = trim(1151,1150)
You've got an erroneous extra scene at the end, because you should be using isb=false (as in frustum's original).

Note also that (as I commented to frustum) none of the variables needs to be global.

If I include the writefilestart code the text file is empty after the encoding finishes.
That suggests your encoder is loading the file twice.
How are you doing the encoding?
A simple way for something like this would be to load the script into VirtualDub and just run the video analysis pass.
May I ask if it's possible to encode once and have multiple output clips from a source file? If so I would like to run the trim list to do this.
No, if you want to encode separate clips for each scene, you will have to run each in a separate script.

videoFred
3rd May 2011, 10:50
I have modified Frustum's script, and now the output is a working Avisynth script instead of a txt file. The output now looks like this:

### scene change list ###

AviSource("C:\Users\Freddy\Documents\02_gerenderd\Expo\Expo_001\expo1.40.avi.avsgerenderd.avi")

trim(9,114) +\
trim(131,245) +\
trim(262,729) +\
trim(746,960) +\
trim(977,1154) +\
trim(1171,1533) +\
trim(1550,1835) +\
trim(1852,1844)

Because I have lots of files, I also have made scripts and VD jobfiles with ScriptWriter15. With a small change in the jobfile, VDub only runs the script without saving any AVI files, while the scene detection script writes the scene change script.

This way, I can automatically run an entire directory of AVI files and at the end of the session I have my scene change scripts, without touching the originals. Then, I load these scripts in VDub and save the results as AVI. No cutting in the originals.. they are still there.

Fred.

Jeremy Duncan
3rd May 2011, 12:49
Here's my new code:



loadplugin("C:\Program Files\AviSynth 2.5\plugins\DirectShowSource.dll")
loadplugin("C:\Program Files\AviSynth 2.5\plugins\mvtools2.dll")

film = "C:\Users\Rudolf\Desktop\121819956.vc1"
source = DirectShowSource(film).converttoYV12()
min_scene_size = 5
trim_front = 0
trim_end = 0
scene_num = 1
scene_begin = 0
scene_end = -1
scheme = 2

vectors = source.MSuper(pel=2, hpad=12, vpad=12, rfilter=2, isse=true).MAnalyse(isb=false, search=3, searchparam=3, isse=true, lambda=440, dct=4)
source.MSCDetection(vectors, thscd1=248, thscd2=50)

# Log results

file_out = "C:\Users\Rudolf\Desktop\cuts.txt"

WriteFileStart(file_out, """ "### scene change list ###" + chr(10) + \
"directshowsource("+film+")" """)
FrameEvaluate(last, """

# detect new scene
global newscene = (current_frame < scene_end + min_scene_size) ? false \
: (scheme == 1) ? (YDifferenceFromPrevious>40) \
: (AverageLuma>250)

# remember where scene started for subsequent filtering.
# this probably doesn't work well if not accesing the clip linearly.
scene_begin = newscene ? scene_end+1 : scene_begin
scene_end = newscene ? current_frame : scene_end

# bump scene counter on each scene change
scene_num = (newscene && (current_frame > 0)) ? scene_num+1 : scene_num
""")


WriteFileif(last, file_out, "newscene", \
"""" trim("""", "scene_begin + trim_front", """","""", "scene_end - trim_end", """") """", """"+"""", """"\"""")

# log scene changes at end of film
WriteFileif(file_out, "current_frame==framecount-1", \
"""" trim("""", "scene_end + 1 + trim_front", """","""", "framecount-1-trim_end", """")"""")

ShowFrameNumber(size=12, x=10, y=40)

ScriptClip(last, """subtitle("scene #"+string(scene_num))""")

converttoyv12()

Here's my new trim file results:


### scene change list ###
directshowsource(C:\Users\Rudolf\Desktop\121819956.vc1)
trim(0,93) +\
trim(94,153) +\
trim(154,197) +\
trim(198,249) +\
trim(250,287) +\
trim(288,331) +\
trim(332,398) +\
trim(399,420) +\
trim(421,467) +\
trim(468,485) +\
trim(486,512) +\
trim(513,526) +\
trim(527,540) +\
trim(541,592) +\
trim(593,633) +\
trim(634,653) +\
trim(654,688) +\
trim(689,714) +\
trim(715,787) +\
trim(788,848) +\
trim(849,880) +\
trim(881,925) +\
trim(926,942) +\
trim(943,966) +\
trim(967,975) +\
trim(976,995) +\
trim(996,1012) +\
trim(1013,1027) +\
trim(1028,1036) +\
trim(1037,1055) +\
trim(1056,1075) +\
trim(1076,1150)



I tried to get the quotation marks in the text file; directshowsource line, but I couldn't do it. I was using megui on default settings, and just loading the avs file, but Virtualdub analysis pass worked, that's how I got the text file I just posted.

real.finder
8th May 2013, 22:17
My edits to make it work at the end of any Script


source = last
min_scene_size = 5
trim_front = 0
trim_end = 0
scene_num = 1
scene_begin = 0
scene_end = -1
scheme = 2

vectors = source.MSuper(pel=2, hpad=12, vpad=12, rfilter=2, isse=true).MAnalyse(isb=false, search=3, searchparam=3, isse=true, lambda=440, dct=4)
source.MSCDetection(vectors, thscd1=248, thscd2=50)

# Log results

file_out = "scene changes.txt"

WriteFileStart(file_out, "### scene change list ###" + chr(10))
FrameEvaluate(last, """

# detect new scene
global newscene = (current_frame < scene_end + min_scene_size) ? false \
: (scheme == 1) ? (YDifferenceFromPrevious>40) \
: (AverageLuma>250)

# remember where scene started for subsequent filtering.
# this probably doesn't work well if not accesing the clip linearly.
scene_begin = newscene ? scene_end+1 : scene_begin
scene_end = newscene ? current_frame-1 : scene_end

# bump scene counter on each scene change
scene_num = (newscene && (current_frame > 0)) ? scene_num+1 : scene_num
""")


WriteFileif(last, file_out, "newscene", \
"""" trim("""", "scene_begin + trim_front", """","""", "scene_end - trim_end", """") """", """"+"""", """"\"""")

# log scene changes at end of film
WriteFileif(file_out, "current_frame==framecount-1", \
"""" trim("""", "scene_end + 1 + trim_front", """","""", "framecount-1-trim_end", """")"""")

ShowFrameNumber(size=12, x=10, y=40)

ScriptClip(last, """subtitle("scene #"+string(scene_num))""")