Spaces:
Running
on
Zero
Running
on
Zero
File size: 6,897 Bytes
b09e573 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import numpy as np
import cv2
import time
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
Logger = logging.getLogger(__name__)
def MergeBoxes(Boxes, Padding=5):
if len(Boxes) <= 1:
return Boxes
MergedOccurred = True
while MergedOccurred:
MergedOccurred = False
NewBoxes = []
Boxes.sort(key=lambda b: b[0])
Used = [False] * len(Boxes)
for Index in range(len(Boxes)):
if Used[Index]:
continue
CurrentBox = list(Boxes[Index])
Used[Index] = True
for J in range(Index + 1, len(Boxes)):
if Used[J]:
continue
NextBox = Boxes[J]
OverlapX = max(CurrentBox[0], NextBox[0]) <= min(CurrentBox[0] + CurrentBox[2], NextBox[0] + NextBox[2]) + Padding
OverlapY = max(CurrentBox[1], NextBox[1]) <= min(CurrentBox[1] + CurrentBox[3], NextBox[1] + NextBox[3]) + Padding
if OverlapX and OverlapY:
NewX = min(CurrentBox[0], NextBox[0])
NewY = min(CurrentBox[1], NextBox[1])
NewW = max(CurrentBox[0] + CurrentBox[2], NextBox[0] + NextBox[2]) - NewX
NewH = max(CurrentBox[1] + CurrentBox[3], NextBox[1] + NextBox[3]) - NewY
CurrentBox = [NewX, NewY, NewW, NewH]
Used[J] = True
MergedOccurred = True
NewBoxes.append(tuple(CurrentBox))
Boxes = NewBoxes
return Boxes
def GetChangeMask(Image1, Image2, Threshold=25, MinArea=100):
if Image1.shape != Image2.shape:
Logger.warning(f'Image shapes differ: {Image1.shape} vs {Image2.shape}. Resizing Image2.')
Image2 = cv2.resize(Image2, (Image1.shape[1], Image1.shape[0]))
Gray1 = cv2.cvtColor(Image1, cv2.COLOR_BGR2GRAY)
Gray2 = cv2.cvtColor(Image2, cv2.COLOR_BGR2GRAY)
Blur1 = cv2.GaussianBlur(Gray1, (5, 5), 0)
Blur2 = cv2.GaussianBlur(Gray2, (5, 5), 0)
DiffFrame = cv2.absdiff(Blur1, Blur2)
_, ThresholdCalc = cv2.threshold(DiffFrame, Threshold, 255, cv2.THRESH_BINARY)
Kernel = np.ones((5, 5), np.uint8)
DilatedThreshold = cv2.dilate(ThresholdCalc, Kernel, iterations=2)
Contours, _ = cv2.findContours(DilatedThreshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
OutputMask = np.zeros_like(DilatedThreshold)
ValidContours = 0
if Contours:
for Contour in Contours:
if cv2.contourArea(Contour) > MinArea:
cv2.drawContours(OutputMask, [Contour], -1, 255, -1) # type: ignore
ValidContours +=1
Logger.info(f'GetChangeMask: Found {len(Contours)} raw contours, kept {ValidContours} after MinArea filter ({MinArea}).')
return OutputMask
def VisualizeDifferences(Image1Path, Image2Path, OutputPath, Threshold=25, MinArea=100, OutlineColor=(0, 255, 0), FillColor=(0, 180, 0), FillAlpha=0.3):
Logger.info(f'π¨ Visualizing differences between {Image1Path} and {Image2Path}')
Image1 = cv2.imread(Image1Path)
Image2 = cv2.imread(Image2Path)
if Image1 is None or Image2 is None:
Logger.error(f'β Error loading images for visualization: {Image1Path} or {Image2Path}')
return
if Image1.shape != Image2.shape:
Logger.warning(f'β οΈ Image shapes differ: {Image1.shape} vs {Image2.shape}. Resizing Image2 for visualization.')
Image2 = cv2.resize(Image2, (Image1.shape[1], Image1.shape[0]))
ChangedMask = GetChangeMask(Image1, Image2, Threshold, MinArea)
OutputImage = Image2.copy()
Overlay = OutputImage.copy()
# Apply fill color to changed areas
Overlay[ChangedMask == 255] = FillColor
cv2.addWeighted(Overlay, FillAlpha, OutputImage, 1 - FillAlpha, 0, OutputImage)
# Find contours of the changed areas to draw outlines
Contours, _ = cv2.findContours(ChangedMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(OutputImage, Contours, -1, OutlineColor, 2)
Logger.info(f'π¨ Drew {len(Contours)} difference regions.')
try:
cv2.imwrite(OutputPath, OutputImage)
Logger.info(f'πΎ Saved difference visualization to {OutputPath}')
except Exception as E:
Logger.error(f'β Failed to save visualization {OutputPath}: {E}')
# --- Function to be used in App.py for upscaling ---
def GetChangedRegions(Image1, Image2, Threshold=25, Padding=10, MinArea=100, MergePadding=5):
StartTime = time.time()
Logger.info('π Comparing images...')
if Image1 is None or Image2 is None:
Logger.error('β Cannot compare None images.')
return []
if Image1.shape != Image2.shape:
Logger.warning(f'β οΈ Image shapes differ: {Image1.shape} vs {Image2.shape}. Resizing Image2.')
Image2 = cv2.resize(Image2, (Image1.shape[1], Image1.shape[0]))
Gray1 = cv2.cvtColor(Image1, cv2.COLOR_BGR2GRAY)
Gray2 = cv2.cvtColor(Image2, cv2.COLOR_BGR2GRAY)
Blur1 = cv2.GaussianBlur(Gray1, (5, 5), 0)
Blur2 = cv2.GaussianBlur(Gray2, (5, 5), 0)
DiffFrame = cv2.absdiff(Blur1, Blur2)
_, ThresholdCalc = cv2.threshold(DiffFrame, Threshold, 255, cv2.THRESH_BINARY)
Kernel = np.ones((5, 5), np.uint8)
DilatedThreshold = cv2.dilate(ThresholdCalc, Kernel, iterations=2)
Contours, _ = cv2.findContours(DilatedThreshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Logger.info(f'π Found {len(Contours)} raw contours.')
BoundingBoxes = []
if Contours:
ValidContours = 0
for Contour in Contours:
ContourArea = cv2.contourArea(Contour)
if ContourArea > MinArea:
ValidContours += 1
X, Y, W, H = cv2.boundingRect(Contour)
PaddedX = max(0, X - Padding)
PaddedY = max(0, Y - Padding)
MaxW = Image1.shape[1] - PaddedX
MaxH = Image1.shape[0] - PaddedY
PaddedW = min(W + (Padding * 2), MaxW)
PaddedH = min(H + (Padding * 2), MaxH)
BoundingBoxes.append((PaddedX, PaddedY, PaddedW, PaddedH))
Logger.info(f'π Filtered {ValidContours} contours based on MinArea ({MinArea}).')
InitialBoxCount = len(BoundingBoxes)
MergedBoundingBoxes = MergeBoxes(BoundingBoxes, MergePadding)
EndTime = time.time()
if MergedBoundingBoxes:
Logger.info(f'π¦ Merged {InitialBoxCount} boxes into {len(MergedBoundingBoxes)} regions.')
else:
Logger.info('β No significant changed regions found after filtering and merging.')
Logger.info(f'β±οΈ Region finding took {EndTime - StartTime:.3f}s')
return MergedBoundingBoxes
# Example call for the new visualization function
VisualizeDifferences(r'C:\Users\joris\Pictures\frame_01660.png', r'C:\Users\joris\Pictures\frame_01661.png', './Diff.png', 25, 100, (0, 255, 0), (0, 180, 0), 0.3) |