Detects fast cuts using changes in colour and intensity between frames. The difference is calculated in the HSV color space, and compared against a set threshold to determine when a fast cut has occurred.
| 47 | |
| 48 | |
| 49 | class ContentDetector(SceneDetector): |
| 50 | """Detects fast cuts using changes in colour and intensity between frames. |
| 51 | |
| 52 | The difference is calculated in the HSV color space, and compared against a set threshold to |
| 53 | determine when a fast cut has occurred. |
| 54 | """ |
| 55 | |
| 56 | # TODO: Come up with some good weights for a new default if there is one that can pass |
| 57 | # a wider variety of test cases. |
| 58 | class Components(ty.NamedTuple): |
| 59 | """Components that make up a frame's score, and their default values.""" |
| 60 | |
| 61 | delta_hue: float = 1.0 |
| 62 | """Difference between pixel hue values of adjacent frames.""" |
| 63 | delta_sat: float = 1.0 |
| 64 | """Difference between pixel saturation values of adjacent frames.""" |
| 65 | delta_lum: float = 1.0 |
| 66 | """Difference between pixel luma (brightness) values of adjacent frames.""" |
| 67 | delta_edges: float = 0.0 |
| 68 | """Difference between calculated edges of adjacent frames. |
| 69 | |
| 70 | Edge differences are typically larger than the other components, so the detection |
| 71 | threshold may need to be adjusted accordingly.""" |
| 72 | |
| 73 | DEFAULT_COMPONENT_WEIGHTS = Components() |
| 74 | """Default component weights. Actual default values are specified in :class:`Components` |
| 75 | to allow adding new components without breaking existing usage.""" |
| 76 | |
| 77 | LUMA_ONLY_WEIGHTS = Components( |
| 78 | delta_hue=0.0, |
| 79 | delta_sat=0.0, |
| 80 | delta_lum=1.0, |
| 81 | delta_edges=0.0, |
| 82 | ) |
| 83 | """Component weights to use if `luma_only` is set.""" |
| 84 | |
| 85 | FRAME_SCORE_KEY = "content_val" |
| 86 | """Key in statsfile representing the final frame score after weighed by specified components.""" |
| 87 | |
| 88 | METRIC_KEYS: ty.ClassVar[list[str]] = [FRAME_SCORE_KEY, *Components._fields] |
| 89 | """All statsfile keys this detector produces.""" |
| 90 | |
| 91 | @dataclass |
| 92 | class _FrameData: |
| 93 | """Data calculated for a given frame.""" |
| 94 | |
| 95 | hue: numpy.ndarray |
| 96 | """Frame hue map [2D 8-bit].""" |
| 97 | sat: numpy.ndarray |
| 98 | """Frame saturation map [2D 8-bit].""" |
| 99 | lum: numpy.ndarray |
| 100 | """Frame luma/brightness map [2D 8-bit].""" |
| 101 | edges: numpy.ndarray | None |
| 102 | """Frame edge map [2D 8-bit, edges are 255, non edges 0]. Affected by `kernel_size`.""" |
| 103 | |
| 104 | def __init__( |
| 105 | self, |
| 106 | threshold: float = 27.0, |