Spaces:
Running
Running
| import numpy as np | |
| import cv2 | |
| # Segmented Absolute Difference (SAD) | |
| # Compares two frames, highlights differences in segments, and returns rectangles of changed areas | |
| def HighlightDifferences(BaseFrame: np.ndarray, NextFrame: np.ndarray, Columns: int = 20, Rows: int = 12, Threshold: float = 10, Padding: int = 1): | |
| FrameHeight, FrameWidth = BaseFrame.shape[:2] | |
| SegmentWidth = FrameWidth // Columns | |
| SegmentHeight = FrameHeight // Rows | |
| HighlightedFrame = BaseFrame.copy() | |
| TotalSegments = 0 | |
| SimilarSegments = 0 | |
| DifferentSegments = 0 | |
| DifferentSegmentMask = np.zeros((Rows, Columns), dtype=bool) | |
| for Row in range(Rows): | |
| for Col in range(Columns): | |
| Y = Row * SegmentHeight | |
| X = Col * SegmentWidth | |
| Y2 = FrameHeight if Row == Rows - 1 else Y + SegmentHeight | |
| X2 = FrameWidth if Col == Columns - 1 else X + SegmentWidth | |
| TotalSegments += 1 | |
| SegmentBase = BaseFrame[Y:Y2, X:X2] | |
| SegmentNext = NextFrame[Y:Y2, X:X2] | |
| GreyBase = cv2.cvtColor(SegmentBase, cv2.COLOR_BGR2GRAY) | |
| GreyNext = cv2.cvtColor(SegmentNext, cv2.COLOR_BGR2GRAY) | |
| BlurredBase = cv2.GaussianBlur(GreyBase, (5, 5), 0) | |
| BlurredNext = cv2.GaussianBlur(GreyNext, (5, 5), 0) | |
| AbsDiff = cv2.absdiff(BlurredBase, BlurredNext) | |
| MeanDiff = np.mean(AbsDiff) # type: ignore | |
| if MeanDiff > Threshold: | |
| DifferentSegments += 1 | |
| DifferentSegmentMask[Row, Col] = True | |
| else: | |
| SimilarSegments += 1 | |
| PaddedMask = DifferentSegmentMask.copy() | |
| for Row in range(Rows): | |
| for Col in range(Columns): | |
| if DifferentSegmentMask[Row, Col]: | |
| for PR in range(max(0, Row - Padding), min(Rows, Row + Padding + 1)): | |
| for PC in range(max(0, Col - Padding), min(Columns, Col + Padding + 1)): | |
| PaddedMask[PR, PC] = True | |
| for Row in range(Rows): | |
| for Col in range(Columns): | |
| Y = Row * SegmentHeight | |
| X = Col * SegmentWidth | |
| Y2 = FrameHeight if Row == Rows - 1 else Y + SegmentHeight | |
| X2 = FrameWidth if Col == Columns - 1 else X + SegmentWidth | |
| SegmentBase = BaseFrame[Y:Y2, X:X2] | |
| if PaddedMask[Row, Col]: | |
| HighlightedFrame[Y:Y2, X:X2] = cv2.addWeighted( | |
| HighlightedFrame[Y:Y2, X:X2], 0.5, | |
| np.full_like(SegmentBase, (0, 0, 255)), 0.2, 0 | |
| ) | |
| else: | |
| HighlightedFrame[Y:Y2, X:X2] = cv2.addWeighted( | |
| HighlightedFrame[Y:Y2, X:X2], 0.5, | |
| np.full_like(SegmentBase, (0, 255, 0)), 0.2, 0 | |
| ) | |
| SimilarityPercentage = (SimilarSegments / TotalSegments) * 100 | |
| TileCoords = [] | |
| for Row in range(Rows): | |
| for Col in range(Columns): | |
| if PaddedMask[Row, Col]: | |
| TileCoords.append((Col, Row)) | |
| return HighlightedFrame, DifferentSegments, SimilarityPercentage, TileCoords | |
| def GetRectanglesFromTiles(TileMask: np.ndarray, MinDifferentRatio: float = 0.8): | |
| Height, Width = TileMask.shape | |
| Visited = np.zeros_like(TileMask, dtype=bool) | |
| Rectangles = [] | |
| for Y in range(Height): | |
| for X in range(Width): | |
| if TileMask[Y, X] and not Visited[Y, X]: | |
| W = 1 | |
| H = 1 | |
| Expand = True | |
| while Expand: | |
| Expand = False | |
| if X + W < Width: | |
| NewCol = TileMask[Y:Y+H, X+W] & ~Visited[Y:Y+H, X+W] | |
| if np.any(NewCol): | |
| NewRect = TileMask[Y:Y+H, X:X+W+1] & ~Visited[Y:Y+H, X:X+W+1] | |
| Total = NewRect.size | |
| Diff = np.count_nonzero(NewRect) | |
| Ratio = Diff / Total | |
| if Ratio >= MinDifferentRatio and not np.any(Visited[Y:Y+H, X+W]): | |
| W += 1 | |
| Expand = True | |
| if Y + H < Height: | |
| NewRow = TileMask[Y+H, X:X+W] & ~Visited[Y+H, X:X+W] | |
| if np.any(NewRow): | |
| NewRect = TileMask[Y:Y+H+1, X:X+W] & ~Visited[Y:Y+H+1, X:X+W] | |
| Total = NewRect.size | |
| Diff = np.count_nonzero(NewRect) | |
| Ratio = Diff / Total | |
| if Ratio >= MinDifferentRatio and not np.any(Visited[Y+H, X:X+W]): | |
| H += 1 | |
| Expand = True | |
| Visited[Y:Y+H, X:X+W] = True | |
| Rectangles.append((X, Y, W, H)) | |
| return Rectangles | |
| def GetDifferenceRectangles(BaseFrame, NextFrame, Columns=20, Rows=12, Threshold=5, Padding=1): | |
| HighlightedFrame, DifferentSegments, SimilarPercentage, TileCoords = HighlightDifferences( | |
| BaseFrame, NextFrame, Columns=Columns, Rows=Rows, Threshold=Threshold, Padding=Padding | |
| ) | |
| TileMask = np.zeros((Rows, Columns), dtype=bool) | |
| for Col, Row in TileCoords: | |
| if Row < TileMask.shape[0] and Col < TileMask.shape[1]: | |
| TileMask[Row, Col] = True | |
| Rectangles = GetRectanglesFromTiles(TileMask, MinDifferentRatio=0.7) | |
| return { | |
| 'HighlightedFrame': HighlightedFrame, | |
| 'Rectangles': Rectangles, | |
| 'SimilarPercentage': SimilarPercentage, | |
| 'TileCoords': TileCoords, | |
| 'Columns': Columns, | |
| 'Rows': Rows | |
| } |