Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions scenedetect/_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,15 @@ def from_config(config_value: str, default: "ScoreWeightsValue") -> "ScoreWeight


class KernelSizeValue(ValidatedValue):
"""Validator for kernel sizes (odd integer > 1, or -1 for auto size)."""
"""Validator for kernel sizes (odd integer >= 3, or -1 for auto size)."""

def __init__(self, value: int):
self._value: int | None
if value == -1:
# Kernel size of -1 maps to None internally for auto-sized kernel.
self._value = None
elif value < 0:
# Disallow other negative values.
elif value < 3:
# Disallow other negative values, 0 and 1 (detectors require an odd integer >= 3).
raise ValueError()
elif value % 2 == 0:
# Disallow even values.
Expand All @@ -273,7 +273,7 @@ def from_config(config_value: str, default: "KernelSizeValue") -> "KernelSizeVal
return KernelSizeValue(int(config_value))
except ValueError as ex:
raise OptionParseFailure(
"Value must be an odd integer greater than 1, or set to -1 for auto kernel size."
"Value must be an odd integer >= 3, or set to -1 for auto kernel size."
) from ex


Expand Down
17 changes: 15 additions & 2 deletions scenedetect/_cli/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ def _resolve_min_scene_len(self, command: str, override: str | None) -> int:
assert parsed is not None
return parsed.frame_num

@staticmethod
def _validate_kernel_size(kernel_size: int | None) -> int | None:
"""Reject a `--kernel-size` the detectors would raise on, so it gives a usage error."""
if kernel_size is not None and (kernel_size < 3 or kernel_size % 2 == 0):
raise click.BadParameter(
"Kernel size must be an odd integer >= 3.", param_hint="kernel-size"
)
return kernel_size

def parse_timecode(self, value: str | None, correct_pts: bool = False) -> FrameTimecode | None:
"""Parses a user input string into a FrameTimecode assuming the given framerate. If `value`
is None it will be passed through without processing.
Expand Down Expand Up @@ -359,7 +368,9 @@ def get_detect_content_params(

return {
"weights": self.config.get_value("detect-content", "weights", weights),
"kernel_size": self.config.get_value("detect-content", "kernel-size", kernel_size),
"kernel_size": self.config.get_value(
"detect-content", "kernel-size", self._validate_kernel_size(kernel_size)
),
"luma_only": luma_only or self.config.get_value("detect-content", "luma-only"),
"min_scene_len": min_scene_len_frames,
"threshold": self.config.get_value("detect-content", "threshold", threshold),
Expand Down Expand Up @@ -391,7 +402,9 @@ def get_detect_adaptive_params(
return {
"adaptive_threshold": self.config.get_value("detect-adaptive", "threshold", threshold),
"weights": self.config.get_value("detect-adaptive", "weights", weights),
"kernel_size": self.config.get_value("detect-adaptive", "kernel-size", kernel_size),
"kernel_size": self.config.get_value(
"detect-adaptive", "kernel-size", self._validate_kernel_size(kernel_size)
),
"luma_only": luma_only or self.config.get_value("detect-adaptive", "luma-only"),
"min_content_val": self.config.get_value(
"detect-adaptive", "min-content-val", min_content_val
Expand Down
25 changes: 25 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,31 @@ def test_cli_frame_rate_aliases(option: str):
assert exit_code == 0


@pytest.mark.parametrize(
"cli_args",
[
["detect-content", "-k", "1"],
["detect-content", "-k", "4"],
["detect-adaptive", "-k", "1"],
],
)
def test_cli_kernel_size_rejects_invalid(cli_args: list[str]):
"""Kernel sizes below 3 or even must be rejected with a usage error, not a traceback."""
exit_code, output = invoke_cli(["-i", DEFAULT_VIDEO_PATH, *cli_args, "time", "-e", "1"])
assert exit_code != 0
assert "Kernel size must be an odd integer >= 3." in output


@pytest.mark.parametrize("command", ["detect-content", "detect-adaptive"])
def test_cli_kernel_size_config_file_rejects_one(tmp_path: Path, command: str):
"""A config file kernel-size of 1 must be rejected when loading, like other invalid sizes."""
config_path = tmp_path.joinpath("config.cfg")
config_path.write_text(f"[{command}]\nkernel-size = 1\n")
with pytest.raises(ConfigLoadFailure) as ex:
ConfigRegistry(str(config_path))
assert any("odd integer >= 3" in message for _, message in ex.value.init_log)


@pytest.mark.parametrize(
("options", "succeeds"),
[
Expand Down