From 914ae29073222ac8dedde7378a9ae2fbb1711d43 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Tue, 23 Dec 2025 09:10:42 +0100 Subject: [PATCH] Refactor all magic numbers to constants Introduce constants for brightness and contrast limits, JPEG quality, and frame difference detection thresholds. Refactor related logic to utilize these constants, improving maintainability and consistency across video editing functionalities. Update UI display settings for better text scaling and overlay transparency. --- croppa/main.py | 92 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/croppa/main.py b/croppa/main.py index 6503ec5..c8a4c56 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -758,7 +758,7 @@ class VideoEditor: # Crop adjustment settings CROP_SIZE_STEP = 5 # pixels to expand/contract crop CROP_MIN_SIZE = 10 # minimum crop width/height in pixels - CROP_BORDER_DETECTION_MAX_DISTANCE = 800 # pixels - maximum distance for border hit detection + CROP_BORDER_DETECTION_MAX_DISTANCE = 8000 # pixels - maximum distance for border hit detection # Motion tracking settings TRACKING_POINT_THRESHOLD = 10 # pixels for delete/snap radius @@ -768,6 +768,37 @@ class VideoEditor: SEEK_FRAMES_SHIFT = 10 # Shift modifier: 10 frames SEEK_FRAMES_DEFAULT = 1 # Default: 1 frame + # Brightness and contrast settings + MIN_BRIGHTNESS = -100 + MAX_BRIGHTNESS = 100 + MIN_CONTRAST = 0.1 + MAX_CONTRAST = 3.0 + + # Image/video quality settings + JPEG_QUALITY = 95 # JPEG quality for screenshots (0-100) + IMAGE_MODE_FPS = 30 # Dummy FPS for image mode + HIGH_FPS_THRESHOLD = 60 # FPS threshold for high FPS detection + + # Frame difference detection settings + FRAME_DIFFERENCE_THRESHOLD_DEFAULT = 10.0 # Percentage threshold for frame difference + FRAME_DIFFERENCE_GAP_DEFAULT = 10 # Number of frames between comparisons + FRAME_DIFFERENCE_PIXEL_THRESHOLD = 30 # Pixel threshold for binary thresholding + + # Template matching settings + TEMPLATE_MATCH_HISTORY_SIZE = 20 # Number of recent matches to keep + TEMPLATE_MATCH_AVERAGE_SIZE = 10 # Number of recent matches for average calculation + TEMPLATE_MATCH_MIN_THRESHOLD = 0.3 # Minimum confidence threshold + TEMPLATE_MATCH_AVERAGE_FACTOR = 0.8 # Factor for adaptive threshold (80% of average) + TEMPLATE_MATCH_DEFAULT_THRESHOLD = 0.5 # Default confidence threshold + + # Search/update intervals + INTERESTING_POINT_SEARCH_UPDATE_INTERVAL = 10 # Frames between progress updates + + # UI display settings + FONT_SCALE_SMALL = 0.5 # Small font scale for UI text + OVERLAY_ALPHA_LOW = 0.3 # Low alpha for transparent overlays + OVERLAY_ALPHA_HIGH = 0.7 # High alpha for semi-transparent overlays + def __init__(self, path: str): self.path = Path(path) @@ -894,8 +925,8 @@ class VideoEditor: self.template_matching_full_frame = False # Toggle for full frame vs cropped template matching # Frame difference for interesting point detection - self.frame_difference_threshold = 10.0 # Percentage threshold for frame difference (10% default) - self.frame_difference_gap = 10 # Number of frames between comparisons (default 10) + self.frame_difference_threshold = self.FRAME_DIFFERENCE_THRESHOLD_DEFAULT + self.frame_difference_gap = self.FRAME_DIFFERENCE_GAP_DEFAULT # Region selection for interesting point detection self.interesting_region = None # (x, y, width, height) or None for full frame @@ -1108,8 +1139,8 @@ class VideoEditor: # Validate and clamp values self.current_frame = max(0, min(self.current_frame, getattr(self, 'total_frames', 1) - 1)) self.zoom_factor = max(self.MIN_ZOOM, min(self.MAX_ZOOM, self.zoom_factor)) - self.brightness = max(-100, min(100, self.brightness)) - self.contrast = max(0.1, min(3.0, self.contrast)) + self.brightness = max(self.MIN_BRIGHTNESS, min(self.MAX_BRIGHTNESS, self.brightness)) + self.contrast = max(self.MIN_CONTRAST, min(self.MAX_CONTRAST, self.contrast)) self.playback_speed = max(self.MIN_PLAYBACK_SPEED, min(self.MAX_PLAYBACK_SPEED, self.playback_speed)) self.seek_multiplier = max(self.MIN_SEEK_MULTIPLIER, min(self.MAX_SEEK_MULTIPLIER, self.seek_multiplier)) @@ -1173,8 +1204,7 @@ class VideoEditor: if processed_frame is not None: # Save the processed frame with high quality settings - # Use JPEG quality 95 (0-100, where 100 is highest quality) - success = cv2.imwrite(str(screenshot_path), processed_frame, [cv2.IMWRITE_JPEG_QUALITY, 95]) + success = cv2.imwrite(str(screenshot_path), processed_frame, [cv2.IMWRITE_JPEG_QUALITY, self.JPEG_QUALITY]) if success: print(f"Screenshot saved: {screenshot_name}") self.show_feedback_message(f"Screenshot saved: {screenshot_name}") @@ -1236,7 +1266,7 @@ class VideoEditor: # Set up image properties to mimic video interface self.frame_height, self.frame_width = self.static_image.shape[:2] self.total_frames = 1 - self.fps = 30 # Dummy FPS for image mode + self.fps = self.IMAGE_MODE_FPS self.cap = None print(f"Loaded image: {self.video_path.name}") @@ -1285,7 +1315,7 @@ class VideoEditor: print(" Warning: Large H.264 video detected - seeking may be slow") if self.frame_width * self.frame_height > 1920 * 1080: print(" Warning: High resolution video - decoding may be slow") - if self.fps > 60: + if self.fps > self.HIGH_FPS_THRESHOLD: print(" Warning: High framerate video - may impact playback smoothness") # Set default values for video-specific properties @@ -1619,7 +1649,7 @@ class VideoEditor: 'base_frame': None, 'base_frame_num': None, 'search_cancelled': False, - 'update_interval': 10 + 'update_interval': self.INTERESTING_POINT_SEARCH_UPDATE_INTERVAL } # Enable search mode for OSD display @@ -1841,7 +1871,7 @@ class VideoEditor: # Calculate percentage of pixels that changed significantly # Use threshold to ignore minor noise - _, thresh_diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY) + _, thresh_diff = cv2.threshold(diff, self.FRAME_DIFFERENCE_PIXEL_THRESHOLD, 255, cv2.THRESH_BINARY) # Count changed pixels changed_pixels = cv2.countNonZero(thresh_diff) @@ -2639,16 +2669,16 @@ class VideoEditor: # Adaptive thresholding based on recent match history if len(self.template_match_history) > 0: # Use average of recent matches as baseline - avg_confidence = sum(self.template_match_history[-10:]) / len(self.template_match_history[-10:]) - threshold = max(0.3, avg_confidence * 0.8) # 80% of recent average, minimum 0.3 + avg_confidence = sum(self.template_match_history[-self.TEMPLATE_MATCH_AVERAGE_SIZE:]) / len(self.template_match_history[-self.TEMPLATE_MATCH_AVERAGE_SIZE:]) + threshold = max(self.TEMPLATE_MATCH_MIN_THRESHOLD, avg_confidence * self.TEMPLATE_MATCH_AVERAGE_FACTOR) else: - threshold = 0.5 # Default threshold + threshold = self.TEMPLATE_MATCH_DEFAULT_THRESHOLD # Only accept matches above adaptive threshold if best_confidence > threshold: # Store confidence for adaptive thresholding self.template_match_history.append(best_confidence) - if len(self.template_match_history) > 20: # Keep only last 20 matches + if len(self.template_match_history) > self.TEMPLATE_MATCH_HISTORY_SIZE: self.template_match_history.pop(0) return best_match else: @@ -2879,13 +2909,13 @@ class VideoEditor: def adjust_brightness(self, delta: int): """Adjust brightness by delta (-100 to 100)""" - self.brightness = max(-100, min(100, self.brightness + delta)) + self.brightness = max(self.MIN_BRIGHTNESS, min(self.MAX_BRIGHTNESS, self.brightness + delta)) self.clear_transformation_cache() self.display_needs_update = True def adjust_contrast(self, delta: float): """Adjust contrast by delta (0.1 to 3.0)""" - self.contrast = max(0.1, min(3.0, self.contrast + delta)) + self.contrast = max(self.MIN_CONTRAST, min(self.MAX_CONTRAST, self.contrast + delta)) self.clear_transformation_cache() self.display_needs_update = True @@ -3038,7 +3068,7 @@ class VideoEditor: # Semi-transparent background overlay = frame.copy() cv2.rectangle(overlay, (rect_x1, rect_y1), (rect_x2, rect_y2), (0, 0, 0), -1) - alpha = 0.7 + alpha = self.OVERLAY_ALPHA_HIGH cv2.addWeighted(overlay, alpha, frame, 1 - alpha, 0, frame) # Draw text with shadow @@ -3117,12 +3147,12 @@ class VideoEditor: # Draw progress percentage on the left percentage_text = f"{self.progress_bar_progress * 100:.1f}%" text_color = tuple(int(255 * fade_alpha) for _ in range(3)) - cv2.putText( + cv2.putText( frame, percentage_text, (bar_x + 12, bar_y + 22), cv2.FONT_HERSHEY_SIMPLEX, - 0.5, + self.FONT_SCALE_SMALL, (0, 0, 0), 4, ) @@ -3131,7 +3161,7 @@ class VideoEditor: percentage_text, (bar_x + 10, bar_y + 20), cv2.FONT_HERSHEY_SIMPLEX, - 0.5, + self.FONT_SCALE_SMALL, text_color, 2, ) @@ -3139,7 +3169,7 @@ class VideoEditor: # Draw FPS on the right if available if self.progress_bar_fps > 0: fps_text = f"{self.progress_bar_fps:.1f} FPS" - fps_text_size = cv2.getTextSize(fps_text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)[ + fps_text_size = cv2.getTextSize(fps_text, cv2.FONT_HERSHEY_SIMPLEX, self.FONT_SCALE_SMALL, 1)[ 0 ] fps_x = bar_x + bar_width - fps_text_size[0] - 10 @@ -3148,7 +3178,7 @@ class VideoEditor: fps_text, (fps_x + 2, bar_y + 22), cv2.FONT_HERSHEY_SIMPLEX, - 0.5, + self.FONT_SCALE_SMALL, (0, 0, 0), 4, ) @@ -3157,7 +3187,7 @@ class VideoEditor: fps_text, (fps_x, bar_y + 20), cv2.FONT_HERSHEY_SIMPLEX, - 0.5, + self.FONT_SCALE_SMALL, text_color, 2, ) @@ -3165,7 +3195,7 @@ class VideoEditor: # Draw main text in center if self.progress_bar_text: text_size = cv2.getTextSize( - self.progress_bar_text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1 + self.progress_bar_text, cv2.FONT_HERSHEY_SIMPLEX, self.FONT_SCALE_SMALL, 1 )[0] text_x = bar_x + (bar_width - text_size[0]) // 2 text_y = bar_y + 20 @@ -3570,7 +3600,7 @@ class VideoEditor: cv2.circle(canvas, (sx, sy), 8, (255, 255, 255), 2) # Draw confidence text conf_text = f"{confidence:.2f}" - cv2.putText(canvas, conf_text, (sx + 10, sy - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) + cv2.putText(canvas, conf_text, (sx + 10, sy - 10), cv2.FONT_HERSHEY_SIMPLEX, self.FONT_SCALE_SMALL, (255, 255, 255), 1) # Draw selection rectangles for feature extraction/deletion @@ -3628,7 +3658,7 @@ class VideoEditor: cv2.line(overlay, (sx2, sy2), (arrow_x1, arrow_y1), (255, 255, 0), 1) cv2.line(overlay, (sx2, sy2), (arrow_x2, arrow_y2), (255, 255, 0), 1) - cv2.addWeighted(overlay, 0.3, canvas, 0.7, 0, canvas) # Very transparent + cv2.addWeighted(overlay, self.OVERLAY_ALPHA_LOW, canvas, self.OVERLAY_ALPHA_HIGH, 0, canvas) # Previous tracking point (red) - from the most recent frame with tracking points before current if prev_result: @@ -3689,7 +3719,7 @@ class VideoEditor: # Semi-transparent background overlay = canvas.copy() cv2.rectangle(overlay, (bg_x, bg_y), (bg_x + bg_w, bg_y + bg_h), (0, 0, 0), -1) - cv2.addWeighted(overlay, 0.7, canvas, 0.3, 0, canvas) + cv2.addWeighted(overlay, self.OVERLAY_ALPHA_HIGH, canvas, self.OVERLAY_ALPHA_LOW, 0, canvas) # Border cv2.rectangle(canvas, (bg_x, bg_y), (bg_x + bg_w, bg_y + bg_h), (255, 255, 0), 2) @@ -3749,7 +3779,7 @@ class VideoEditor: # Draw selection info info_text = f"Region: {sel_w}x{sel_h}" - cv2.putText(canvas, info_text, (sel_x, sel_y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1) + cv2.putText(canvas, info_text, (sel_x, sel_y - 5), cv2.FONT_HERSHEY_SIMPLEX, self.FONT_SCALE_SMALL, (0, 255, 255), 1) window_title = "Image Editor" if self.is_image_mode else "Video Editor" cv2.imshow(window_title, canvas) @@ -4639,7 +4669,7 @@ class VideoEditor: if processed_image is not None: # Save the image with high quality settings - success = cv2.imwrite(output_path, processed_image, [cv2.IMWRITE_JPEG_QUALITY, 95]) + success = cv2.imwrite(output_path, processed_image, [cv2.IMWRITE_JPEG_QUALITY, self.JPEG_QUALITY]) if success: print(f"Image saved successfully to {output_path}") return True @@ -5193,7 +5223,7 @@ class VideoEditor: print(f"Frame difference threshold: {self.frame_difference_threshold:.1f}% (-10pp)") self.show_feedback_message(f"Threshold: {self.frame_difference_threshold:.1f}% (-10pp)") elif key == ord("="): # Shift+0 - Increase frame difference threshold by 10 percentage points - self.frame_difference_threshold = min(100.0, self.frame_difference_threshold + 10.0) + self.frame_difference_threshold = min(100.0, self.frame_difference_threshold + 10.0) # Max 100% print(f"Frame difference threshold: {self.frame_difference_threshold:.1f}% (+10pp)") self.show_feedback_message(f"Threshold: {self.frame_difference_threshold:.1f}% (+10pp)") elif key == ord("7"): # 7 - Decrease frame difference gap