Compares the difference in the Y channel of YUV histograms for adjacent frames. When the difference exceeds a given threshold, a cut is detected.
| 25 | |
| 26 | |
| 27 | class HistogramDetector(SceneDetector): |
| 28 | """Compares the difference in the Y channel of YUV histograms for adjacent frames. When the |
| 29 | difference exceeds a given threshold, a cut is detected.""" |
| 30 | |
| 31 | METRIC_KEYS: ty.ClassVar[list[str]] = ["hist_diff"] |
| 32 | |
| 33 | def __init__( |
| 34 | self, |
| 35 | threshold: float = 0.05, |
| 36 | bins: int = 256, |
| 37 | min_scene_len: TimecodeLike = 15, |
| 38 | ): |
| 39 | """ |
| 40 | Arguments: |
| 41 | threshold: maximum relative difference between 0.0 and 1.0 that the histograms can |
| 42 | differ. Histograms are calculated on the Y channel after converting the frame to |
| 43 | YUV, and normalized based on the number of bins. Higher differences imply greater |
| 44 | change in content, so larger threshold values are less sensitive to cuts. |
| 45 | bins: Number of bins to use for the histogram. |
| 46 | min_scene_len: Once a cut is detected, this much time must pass before a new one can |
| 47 | be added to the scene list. Accepts any :data:`TimecodeLike` value. |
| 48 | """ |
| 49 | super().__init__() |
| 50 | # Internally, threshold represents the correlation between two histograms and has values |
| 51 | # between -1.0 and 1.0. |
| 52 | self._threshold = max(0.0, min(1.0, 1.0 - threshold)) |
| 53 | self._bins = bins |
| 54 | self._min_scene_len = min_scene_len |
| 55 | self._last_hist = None |
| 56 | self._last_cut = None |
| 57 | self._metric_key = f"hist_diff [bins={self._bins}]" |
| 58 | |
| 59 | def process_frame( |
| 60 | self, timecode: FrameTimecode, frame_img: numpy.ndarray |
| 61 | ) -> list[FrameTimecode]: |
| 62 | """Computes the histogram of the luma channel of the frame image and compares it with the |
| 63 | histogram of the luma channel of the previous frame. If the difference between the |
| 64 | histograms exceeds the threshold, a scene cut is detected. |
| 65 | Histogram difference is computed using the correlation metric. |
| 66 | |
| 67 | Arguments: |
| 68 | timecode: Timecode of the frame that is being passed. |
| 69 | frame_img: Decoded frame image (numpy.ndarray) to perform scene |
| 70 | detection on. |
| 71 | |
| 72 | Returns: |
| 73 | List of timecodes where scene cuts have been detected. There may be 0 |
| 74 | or more timecodes in the list, and not necessarily the same as `timecode`. |
| 75 | """ |
| 76 | cut_list = [] |
| 77 | |
| 78 | np_data_type = frame_img.dtype |
| 79 | |
| 80 | if np_data_type != numpy.uint8: |
| 81 | raise ValueError("Image must be 8-bit rgb for HistogramDetector") |
| 82 | |
| 83 | if frame_img.shape[2] != 3: |
| 84 | raise ValueError("Image must have three color channels for HistogramDetector") |
nothing calls this directly
no outgoing calls
no test coverage detected