Skip to content

Commit df85d7d

Browse files
committed
Revert "[cli] Add --filter-mode and replace --drop-short-scenes"
Reason: Moving to a detector-only option. This reverts commit 894297d.
1 parent 09057df commit df85d7d

File tree

12 files changed

+235
-223
lines changed

12 files changed

+235
-223
lines changed

scenedetect.cfg

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,16 @@
3939
# Method to use for downscaling (nearest, linear, cubic, area, lanczos4).
4040
#downscale-method = linear
4141

42-
# Minimum length of a given scene. See filter-mode to control how this is enforced.
42+
# Minimum length of a given scene.
4343
#min-scene-len = 0.6s
4444

45-
# Mode to use when filtering out scenes (merge, suppress, drop):
46-
# merge: Consecutive scenes shorter than min-scene-len are combined.
47-
# suppress: No new scenes can be generated until min-scene-len passes.
48-
# drop: Drop all scenes shorter than global min-scene-len.
49-
#filter-mode = merge
50-
51-
# If the video ends less than min-scene-len after the last cut, merge it with the
52-
# previous scene (yes/no).
45+
# Merge last scene if it is shorter than min-scene-len (yes/no). This can occur
46+
# when a cut is detected just before the video ends.
5347
#merge-last-scene = no
5448

49+
# Drop scenes shorter than min-scene-len instead of merging (yes/no).
50+
#drop-short-scenes = no
51+
5552
# Verbosity of console output (debug, info, warning, error, or none).
5653
# Set to none for the same behavior as specifying -q/--quiet.
5754
#verbosity = debug
@@ -68,6 +65,14 @@
6865
# Sensitivity threshold from 0 to 255. Lower values are more sensitive.
6966
#threshold = 27
7067

68+
# Minimum length of a given scene (overrides [global] option).
69+
#min-scene-len = 0.6s
70+
71+
# Mode to use when filtering scenes to comply with min-scene-len:
72+
# merge: Consecutive scenes shorter than min-scene-len are combined.
73+
# suppress: No new scenes can be generated until min-scene-len passes.
74+
#filter-mode = merge
75+
7176
# Weight to place on each component when calculating frame score (the value
7277
# `threshold` is compared against). The components are in the order
7378
# (delta_hue, delta_sat, delta_lum, delta_edges). Description of components:
@@ -87,10 +92,6 @@
8792
# than or equal to 3. If None, automatically set using video resolution.
8893
#kernel-size = -1
8994

90-
# Minimum length of a given scene. No new cuts can be emitted after one is found
91-
# until this length of time passes.
92-
#min-scene-len = 0.6s
93-
9495

9596
[detect-threshold]
9697
# Average pixel intensity from 0-255 at which a fade event is triggered.
@@ -106,8 +107,7 @@
106107
# Discard colour information and only use luminance (yes/no).
107108
#luma-only = no
108109

109-
# Minimum length of a given scene. No new cuts can be emitted after one is found
110-
# until this length of time passes.
110+
# Minimum length of a given scene (overrides [global] option).
111111
#min-scene-len = 0.6s
112112

113113

@@ -121,8 +121,7 @@
121121
# Window size (number of frames) before and after each frame to average together.
122122
#frame-window = 2
123123

124-
# Minimum length of a given scene. No new cuts can be emitted after one is found
125-
# until this length of time passes.
124+
# Minimum length of a given scene (overrides [global] option).
126125
#min-scene-len = 0.6s
127126

128127
# The following parameters are the those used to calculate `content_val`.
@@ -144,8 +143,7 @@
144143
# Number of bits to use for image quantization before binning.
145144
#bits = 4
146145

147-
# Minimum length of a given scene. No new cuts can be emitted after one is found
148-
# until this length of time passes.
146+
# Minimum length of a given scene (overrides [global] option).
149147
#min-scene-len = 0.6s
150148

151149

scenedetect/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
# Used for module identification and when printing version & about info
4949
# (e.g. calling `scenedetect version` or `scenedetect about`).
50-
__version__ = '0.6.4-dev0'
50+
__version__ = '0.7-dev0'
5151

5252
init_logger()
5353
logger = getLogger('pyscenedetect')

scenedetect/_cli/__init__.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,10 @@ def _print_command_help(ctx: click.Context, command: click.Command):
206206
help='Minimum length of any scene. TIMECODE can be specified as number of frames (-m=10), time in seconds (-m=2.5), or timecode (-m=00:02:53.633).%s'
207207
% USER_CONFIG.get_help_string("global", "min-scene-len"),
208208
)
209-
@click.option(
210-
"--filter-mode",
211-
metavar="MODE",
212-
type=click.Choice(CHOICE_MAP["global"]["filter-mode"], False),
213-
default=None,
214-
help='Mode used when enforcing min-scene-len. MODE must be one of: %s. %s' % (', '.join(
215-
CHOICE_MAP["global"]["filter-mode"]), USER_CONFIG.get_help_string("global", "filter-mode")),
216-
)
217209
@click.option(
218210
'--drop-short-scenes',
219211
is_flag=True,
220212
flag_value=True,
221-
hidden=True,
222213
help='Drop scenes shorter than -m/--min-scene-len, instead of combining with neighbors.%s' %
223214
(USER_CONFIG.get_help_string('global', 'drop-short-scenes')),
224215
)
@@ -290,7 +281,6 @@ def scenedetect(
290281
config: Optional[AnyStr],
291282
framerate: Optional[float],
292283
min_scene_len: Optional[str],
293-
filter_mode: Optional[str],
294284
drop_short_scenes: bool,
295285
merge_last_scene: bool,
296286
backend: Optional[str],
@@ -335,7 +325,6 @@ def scenedetect(
335325
downscale=downscale,
336326
frame_skip=frame_skip,
337327
min_scene_len=min_scene_len,
338-
filter_mode=filter_mode,
339328
drop_short_scenes=drop_short_scenes,
340329
merge_last_scene=merge_last_scene,
341330
backend=backend,

scenedetect/_cli/config.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
from scenedetect.detectors import ContentDetector
2929
from scenedetect.frame_timecode import FrameTimecode
30-
from scenedetect.scene_detector import FlashFilter
3130
from scenedetect.scene_manager import Interpolation
3231
from scenedetect.video_splitter import DEFAULT_FFMPEG_ARGS
3332

@@ -233,13 +232,6 @@ def format(self, timecode: FrameTimecode) -> str:
233232
assert False
234233

235234

236-
class FlashFilterMode(Enum):
237-
"""Filter mode for the CLI. Has additional DROP mode which runs as a post-processing step."""
238-
MERGE = FlashFilter.Mode.MERGE
239-
SUPPRESS = FlashFilter.Mode.SUPPRESS
240-
DROP = -1
241-
242-
243235
ConfigValue = Union[bool, int, float, str]
244236
ConfigDict = Dict[str, Dict[str, ConfigValue]]
245237

@@ -251,8 +243,7 @@ class FlashFilterMode(Enum):
251243
DEFAULT_JPG_QUALITY = 95
252244
DEFAULT_WEBP_QUALITY = 100
253245

254-
# TODO(v0.6.4): Warn if [detect-adaptive] min-delta-hsv and [global] drop-short-scenes are used.
255-
# TODO(v0.7): Remove [detect-adaptive] min-delta-hsv and [global] drop-short-scenes
246+
# TODO(v0.7): Remove [detect-adaptive] min-delta-hsv
256247
CONFIG_MAP: ConfigDict = {
257248
'backend-opencv': {
258249
'max-decode-attempts': 5,
@@ -314,7 +305,6 @@ class FlashFilterMode(Enum):
314305
'downscale': 0,
315306
'downscale-method': 'linear',
316307
'drop-short-scenes': False,
317-
'filter-mode': 'merge',
318308
'frame-skip': 0,
319309
'merge-last-scene': False,
320310
'min-scene-len': TimecodeValue('0.6s'),
@@ -358,7 +348,6 @@ class FlashFilterMode(Enum):
358348
'backend': ['opencv', 'pyav', 'moviepy'],
359349
'default-detector': ['detect-adaptive', 'detect-content', 'detect-threshold'],
360350
'downscale-method': [value.name.lower() for value in Interpolation],
361-
'filter-mode': [value.name.lower() for value in FlashFilterMode],
362351
'verbosity': ['debug', 'info', 'warning', 'error', 'none'],
363352
},
364353
'list-scenes': {

scenedetect/_cli/context.py

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@
2323

2424
from scenedetect import open_video, AVAILABLE_BACKENDS
2525

26-
from scenedetect.scene_detector import SceneDetector, FlashFilter
27-
from scenedetect.platform import get_cv2_imwrite_params, init_logger
26+
from scenedetect.scene_detector import SceneDetector
27+
from scenedetect.platform import get_and_create_path, get_cv2_imwrite_params, init_logger
2828
from scenedetect.frame_timecode import FrameTimecode, MAX_FPS_DELTA
2929
from scenedetect.video_stream import VideoStream, VideoOpenFailure, FrameRateUnavailable
3030
from scenedetect.video_splitter import is_mkvmerge_available, is_ffmpeg_available
3131
from scenedetect.detectors import AdaptiveDetector, ContentDetector, ThresholdDetector, HistogramDetector
3232
from scenedetect.stats_manager import StatsManager
3333
from scenedetect.scene_manager import SceneManager, Interpolation
3434

35-
from scenedetect._cli.config import (ConfigRegistry, ConfigLoadFailure, TimecodeFormat,
36-
FlashFilterMode, CHOICE_MAP, DEFAULT_JPG_QUALITY,
37-
DEFAULT_WEBP_QUALITY)
35+
from scenedetect._cli.config import (ConfigRegistry, ConfigLoadFailure, TimecodeFormat, CHOICE_MAP,
36+
DEFAULT_JPG_QUALITY, DEFAULT_WEBP_QUALITY)
3837

3938
logger = logging.getLogger('pyscenedetect')
4039

@@ -117,9 +116,9 @@ def __init__(self):
117116
self.output_dir: str = None # -o/--output
118117
self.quiet_mode: bool = None # -q/--quiet or -v/--verbosity quiet
119118
self.stats_file_path: str = None # -s/--stats
120-
self.min_scene_len: FrameTimecode = None # -m/--min-scene-len
121-
self.filter_mode: FlashFilterMode = None # --filter-mode
119+
self.drop_short_scenes: bool = None # --drop-short-scenes
122120
self.merge_last_scene: bool = None # --merge-last-scene
121+
self.min_scene_len: FrameTimecode = None # -m/--min-scene-len
123122
self.frame_skip: int = None # -fs/--frame-skip
124123
self.default_detector: Tuple[Type[SceneDetector],
125124
Dict[str, Any]] = None # [global] default-detector
@@ -187,7 +186,6 @@ def handle_options(
187186
downscale: Optional[int],
188187
frame_skip: int,
189188
min_scene_len: str,
190-
filter_mode: Optional[str],
191189
drop_short_scenes: bool,
192190
merge_last_scene: bool,
193191
backend: Optional[str],
@@ -271,15 +269,8 @@ def handle_options(
271269
self.min_scene_len = parse_timecode(
272270
min_scene_len if min_scene_len is not None else self.config.get_value(
273271
"global", "min-scene-len"), self.video_stream.frame_rate)
274-
275-
if drop_short_scenes:
276-
logger.warning(
277-
"WARNING: --drop-short-scenes is deprecated, use --filter-mode=drop instead.")
278-
if filter_mode is None:
279-
self.filter_mode = FlashFilterMode.DROP
280-
else:
281-
self.filter_mode = FlashFilterMode[self.config.get_value("global", "filter-mode",
282-
filter_mode).upper()]
272+
self.drop_short_scenes = drop_short_scenes or self.config.get_value(
273+
"global", "drop-short-scenes")
283274
self.merge_last_scene = merge_last_scene or self.config.get_value(
284275
"global", "merge-last-scene")
285276
self.frame_skip = self.config.get_value("global", "frame-skip", frame_skip)
@@ -290,7 +281,6 @@ def handle_options(
290281
self.stats_manager = StatsManager()
291282

292283
# Initialize default detector with values in the config file.
293-
# TODO(v0.6.4): Integrate perceptual hash detector.
294284
default_detector = self.config.get_value("global", "default-detector")
295285
if default_detector == 'detect-adaptive':
296286
self.default_detector = (AdaptiveDetector, self.get_detect_adaptive_params())
@@ -330,18 +320,29 @@ def get_detect_content_params(
330320
) -> Dict[str, Any]:
331321
"""Handle detect-content command options and return dict to construct one with."""
332322
self._ensure_input_open()
323+
324+
if self.drop_short_scenes:
325+
min_scene_len = 0
326+
else:
327+
if min_scene_len is None:
328+
if self.config.is_default('detect-content', 'min-scene-len'):
329+
min_scene_len = self.min_scene_len.frame_num
330+
else:
331+
min_scene_len = self.config.get_value('detect-content', 'min-scene-len')
332+
min_scene_len = parse_timecode(min_scene_len, self.video_stream.frame_rate).frame_num
333+
333334
if weights is not None:
334335
try:
335336
weights = ContentDetector.Components(*weights)
336337
except ValueError as ex:
337338
logger.debug(str(ex))
338-
raise click.BadParameter(str(ex), param_hint="weights")
339+
raise click.BadParameter(str(ex), param_hint='weights')
339340
return {
340-
"weights": self.config.get_value("detect-content", "weights", weights),
341-
"kernel_size": self.config.get_value("detect-content", "kernel-size", kernel_size),
342-
"luma_only": luma_only or self.config.get_value("detect-content", "luma-only"),
343-
"flash_filter": self._init_flash_filter("detect-content", min_scene_len),
344-
"threshold": self.config.get_value("detect-content", "threshold", threshold),
341+
'weights': self.config.get_value('detect-content', 'weights', weights),
342+
'kernel_size': self.config.get_value('detect-content', 'kernel-size', kernel_size),
343+
'luma_only': luma_only or self.config.get_value('detect-content', 'luma-only'),
344+
'min_scene_len': min_scene_len,
345+
'threshold': self.config.get_value('detect-content', 'threshold', threshold),
345346
}
346347

347348
def get_detect_adaptive_params(
@@ -371,8 +372,15 @@ def get_detect_adaptive_params(
371372
self.config.config_dict["detect-adaptive"]["min-content-val"] = (
372373
self.config.config_dict["detect-adaptive"]["min-deleta-hsv"])
373374

374-
# TODO(v0.6.4): Integrate flash filter.
375-
min_scene_len = self._init_flash_filter("detect-adaptive", min_scene_len)._filter_length
375+
if self.drop_short_scenes:
376+
min_scene_len = 0
377+
else:
378+
if min_scene_len is None:
379+
if self.config.is_default("detect-adaptive", "min-scene-len"):
380+
min_scene_len = self.min_scene_len.frame_num
381+
else:
382+
min_scene_len = self.config.get_value("detect-adaptive", "min-scene-len")
383+
min_scene_len = parse_timecode(min_scene_len, self.video_stream.frame_rate).frame_num
376384

377385
if weights is not None:
378386
try:
@@ -406,8 +414,16 @@ def get_detect_threshold_params(
406414
) -> Dict[str, Any]:
407415
"""Handle detect-threshold command options and return dict to construct one with."""
408416
self._ensure_input_open()
409-
# TODO(v0.6.4): Integrate flash filter.
410-
min_scene_len = self._init_flash_filter("detect-adaptive", min_scene_len)._filter_length
417+
418+
if self.drop_short_scenes:
419+
min_scene_len = 0
420+
else:
421+
if min_scene_len is None:
422+
if self.config.is_default("detect-threshold", "min-scene-len"):
423+
min_scene_len = self.min_scene_len.frame_num
424+
else:
425+
min_scene_len = self.config.get_value("detect-threshold", "min-scene-len")
426+
min_scene_len = parse_timecode(min_scene_len, self.video_stream.frame_rate).frame_num
411427
# TODO(v1.0): add_last_scene cannot be disabled right now.
412428
return {
413429
'add_final_scene':
@@ -439,8 +455,15 @@ def get_detect_hist_params(self, threshold: Optional[float], bits: Optional[int]
439455
min_scene_len: Optional[str]) -> Dict[str, Any]:
440456
"""Handle detect-hist command options and return dict to construct one with."""
441457
self._ensure_input_open()
442-
# TODO(v0.6.4): Integrate flash filter.
443-
min_scene_len = self._init_flash_filter("detect-adaptive", min_scene_len)._filter_length
458+
if self.drop_short_scenes:
459+
min_scene_len = 0
460+
else:
461+
if min_scene_len is None:
462+
if self.config.is_default("detect-hist", "min-scene-len"):
463+
min_scene_len = self.min_scene_len.frame_num
464+
else:
465+
min_scene_len = self.config.get_value("detect-hist", "min-scene-len")
466+
min_scene_len = parse_timecode(min_scene_len, self.video_stream.frame_rate).frame_num
444467
return {
445468
'bits': self.config.get_value("detect-hist", "bits", bits),
446469
'min_scene_len': min_scene_len,
@@ -829,15 +852,3 @@ def _on_duplicate_command(self, command: str) -> None:
829852
raise click.BadParameter(
830853
'\n Command %s may only be specified once.' % command,
831854
param_hint='%s command' % command)
832-
833-
def _init_flash_filter(self, detector_name: str,
834-
min_scene_len: ty.Optional[int]) -> FlashFilter:
835-
if self.filter_mode == FlashFilterMode.DROP:
836-
return FlashFilter(length=0)
837-
if min_scene_len is None:
838-
if self.config.is_default(detector_name, 'min-scene-len'):
839-
min_scene_len = self.min_scene_len.frame_num
840-
else:
841-
min_scene_len = self.config.get_value(detector_name, 'min-scene-len')
842-
min_scene_len = parse_timecode(min_scene_len, self.video_stream.frame_rate).frame_num
843-
return FlashFilter(length=min_scene_len, mode=self.filter_mode.value)

scenedetect/_cli/controller.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from scenedetect.video_stream import SeekError
2929

3030
from scenedetect._cli.context import CliContext, check_split_video_requirements
31-
from scenedetect._cli.config import FlashFilterMode
3231

3332
logger = logging.getLogger('pyscenedetect')
3433

@@ -331,13 +330,11 @@ def _postprocess_scene_list(
331330
# it will be merged with the previous one.
332331
if context.merge_last_scene and context.min_scene_len is not None and context.min_scene_len > 0:
333332
if len(scene_list) > 1 and (scene_list[-1][1] - scene_list[-1][0]) < context.min_scene_len:
334-
logger.debug("Last scene is shorter than %d frames, merging with previous.",
335-
context.min_scene_len.get_frames())
336333
new_last_scene = (scene_list[-2][0], scene_list[-1][1])
337334
scene_list = scene_list[:-2] + [new_last_scene]
338335

339-
if context.filter_mode == FlashFilterMode.DROP:
340-
logger.debug("Dropping scenes shorter than %d frames.", context.min_scene_len.get_frames())
336+
# Handle --drop-short-scenes.
337+
if context.drop_short_scenes and context.min_scene_len > 0:
341338
scene_list = [s for s in scene_list if (s[1] - s[0]) >= context.min_scene_len]
342339

343340
return scene_list

0 commit comments

Comments
 (0)