CoolFace
Apppublic

ganeshkumar383/AI-Based-Image-Deblurring-App

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
blur_detection.py681 linesDownload Raw Back to modules
1"""
2Blur Detection Module - Motion vs Defocus Detection
3==================================================
4
5Comprehensive blur analysis using Variance of Laplacian and advanced techniques
6to detect motion blur, defocus blur, and estimate blur parameters.
7"""
8
9import cv2
10import numpy as np
11from scipy import ndimage
12from scipy.signal import find_peaks
13from scipy.fft import fft2, fftshift
14import logging
15from typing import Dict, Tuple, Optional
16
17# Configure logging
18logging.basicConfig(level=logging.INFO)
19logger = logging.getLogger(__name__)
20
21class BlurDetector:
22    """Advanced blur detection and analysis"""
23    
24    def __init__(self):
25        self.sharpness_threshold = {
26            'sharp': 1000,
27            'slightly_blurred': 500,
28            'moderately_blurred': 200,
29            'heavily_blurred': 50
30        }
31    
32    def variance_of_laplacian(self, image: np.ndarray) -> float:
33        """
34        Compute the Laplacian variance (sharpness metric)
35        
36        Args:
37            image: Input image (BGR or grayscale)
38        
39        Returns:
40            float: Variance of Laplacian (higher = sharper)
41        """
42        try:
43            # Convert to grayscale if needed
44            if len(image.shape) == 3:
45                gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
46            else:
47                gray = image.copy()
48            
49            # Compute Laplacian variance
50            laplacian = cv2.Laplacian(gray, cv2.CV_64F)
51            variance = laplacian.var()
52            
53            return variance
54            
55        except Exception as e:
56            logger.error(f"Error computing Laplacian variance: {e}")
57            return 0.0
58    
59    def estimate_motion_blur_params(self, image: np.ndarray) -> Tuple[float, int]:
60        """
61        Estimate motion blur parameters: angle and length
62        
63        Args:
64            image: Input image
65        
66        Returns:
67            tuple: (angle in degrees, length in pixels)
68        """
69        try:
70            # Convert to grayscale
71            if len(image.shape) == 3:
72                gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
73            else:
74                gray = image.copy()
75            
76            # Apply FFT
77            f_transform = np.fft.fft2(gray)
78            f_shift = np.fft.fftshift(f_transform)
79            magnitude_spectrum = np.log(np.abs(f_shift) + 1)
80            
81            # Find dominant direction in frequency domain
82            rows, cols = magnitude_spectrum.shape
83            center_row, center_col = rows // 2, cols // 2
84            
85            # Create radial profile
86            angles = np.linspace(0, 180, 180)
87            max_intensity = 0
88            best_angle = 0
89            
90            for angle in angles:
91                # Create line through center at this angle
92                length = min(rows, cols) // 4
93                x = center_col + length * np.cos(np.radians(angle))
94                y = center_row + length * np.sin(np.radians(angle))
95                
96                # Sample intensity along line
97                if 0 <= x < cols and 0 <= y < rows:
98                    intensity = magnitude_spectrum[int(y), int(x)]
99                    if intensity > max_intensity:
100                        max_intensity = intensity
101                        best_angle = angle
102            
103            # Estimate blur length based on spectrum width
104            # This is a simplified estimation
105            blur_length = max(5, min(50, int(max_intensity / 10)))
106            
107            return best_angle, blur_length
108            
109        except Exception as e:
110            logger.error(f"Error estimating motion blur: {e}")
111            return 0.0, 5
112    
113    def detect_defocus_blur(self, image: np.ndarray) -> float:
114        """
115        Detect defocus blur using edge analysis
116        
117        Args:
118            image: Input image
119        
120        Returns:
121            float: Defocus blur score (0-1, higher = more defocus blur)
122        """
123        try:
124            # Convert to grayscale
125            if len(image.shape) == 3:
126                gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
127            else:
128                gray = image.copy()
129            
130            # Compute gradients
131            grad_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
132            grad_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
133            
134            # Compute gradient magnitude
135            gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
136            
137            # Analyze edge distribution
138            edges = cv2.Canny(gray, 50, 150)
139            edge_density = np.sum(edges > 0) / edges.size
140            
141            # Compute defocus score based on edge characteristics
142            mean_gradient = np.mean(gradient_magnitude)
143            std_gradient = np.std(gradient_magnitude)
144            
145            # Defocus blur typically has lower gradient variation
146            defocus_score = max(0, min(1, 1 - (std_gradient / (mean_gradient + 1e-10))))
147            
148            return defocus_score
149            
150        except Exception as e:
151            logger.error(f"Error detecting defocus blur: {e}")
152            return 0.0
153    
154    def analyze_noise_level(self, image: np.ndarray) -> float:
155        """
156        Estimate noise level in the image
157        
158        Args:
159            image: Input image
160        
161        Returns:
162            float: Estimated noise level (0-1)
163        """
164        try:
165            # Convert to grayscale
166            if len(image.shape) == 3:
167                gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
168            else:
169                gray = image.copy()
170            
171            # Use Laplacian to estimate noise
172            laplacian = cv2.Laplacian(gray, cv2.CV_64F)
173            noise_estimate = np.var(laplacian) / (np.mean(gray) + 1e-10)
174            
175            # Normalize to 0-1 range
176            normalized_noise = min(noise_estimate / 1000, 1.0)
177            
178            return normalized_noise
179            
180        except Exception as e:
181            logger.error(f"Error analyzing noise: {e}")
182            return 0.0
183    
184    def classify_blur_severity(self, sharpness_score: float) -> Tuple[str, float]:
185        """
186        Classify blur severity based on sharpness score
187        
188        Args:
189            sharpness_score: Laplacian variance value
190        
191        Returns:
192            tuple: (severity_label, confidence)
193        """
194        try:
195            if sharpness_score > self.sharpness_threshold['sharp']:
196                return "Sharp", 0.9
197            elif sharpness_score > self.sharpness_threshold['slightly_blurred']:
198                return "Slightly Blurred", 0.8
199            elif sharpness_score > self.sharpness_threshold['moderately_blurred']:
200                return "Moderately Blurred", 0.9
201            elif sharpness_score > self.sharpness_threshold['heavily_blurred']:
202                return "Heavily Blurred", 0.95
203            else:
204                return "Extremely Blurred", 0.98
205                
206        except Exception as e:
207            logger.error(f"Error classifying blur severity: {e}")
208            return "Unknown", 0.0
209    
210    def comprehensive_analysis(self, image: np.ndarray) -> Dict:
211        """
212        Perform comprehensive blur analysis with detailed diagnostics
213        
214        Args:
215            image: Input image
216        
217        Returns:
218            dict: Complete analysis results with detailed explanations
219        """
220        try:
221            # Step 1: Image Properties Analysis
222            height, width = image.shape[:2]
223            channels = image.shape[2] if len(image.shape) == 3 else 1
224            
225            # Step 2: Basic sharpness analysis using Variance of Laplacian
226            sharpness = self.variance_of_laplacian(image)
227            severity, confidence = self.classify_blur_severity(sharpness)
228            
229            # Step 3: Edge Density Analysis
230            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
231            edges = cv2.Canny(gray, 50, 150)
232            edge_density = np.sum(edges > 0) / edges.size
233            
234            # Step 4: Gradient Analysis for sharpness assessment
235            grad_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
236            grad_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
237            gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
238            avg_gradient = np.mean(gradient_magnitude)
239            max_gradient = np.max(gradient_magnitude)
240            
241            # Step 5: Frequency Domain Analysis
242            f_transform = fft2(gray)
243            f_shift = fftshift(f_transform)
244            magnitude_spectrum = np.log(np.abs(f_shift) + 1)
245            high_freq_content = np.mean(magnitude_spectrum[height//4:3*height//4, width//4:3*width//4])
246            
247            # Step 6: Motion blur analysis with detailed parameters
248            motion_angle, motion_length = self.estimate_motion_blur_params(image)
249            
250            # Step 7: Defocus analysis with multiple metrics
251            defocus_score = self.detect_defocus_blur(image)
252            
253            # Step 8: Noise analysis and characterization
254            noise_level = self.analyze_noise_level(image)
255            
256            # Step 9: Contrast and Dynamic Range Analysis
257            hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
258            contrast_measure = np.std(gray)
259            dynamic_range = np.max(gray) - np.min(gray)
260            
261            # Step 10: Texture Analysis using Local Binary Patterns concept
262            texture_variance = np.var(cv2.Laplacian(gray, cv2.CV_64F))
263            
264            # Step 11: Blur Type Classification with Reasoning
265            blur_analysis = self._detailed_blur_classification(
266                sharpness, motion_length, defocus_score, edge_density, 
267                avg_gradient, high_freq_content
268            )
269            
270            # Step 12: Enhancement Recommendation System
271            enhancement_strategy = self._recommend_enhancement_strategy(
272                blur_analysis['primary_type'], severity, noise_level, motion_length
273            )
274            
275            return {
276                # Basic Image Properties
277                'image_dimensions': f"{width}x{height}",
278                'color_channels': channels,
279                'image_size_category': self._categorize_image_size(width, height),
280                
281                # Sharpness and Quality Metrics
282                'sharpness_score': float(sharpness),
283                'sharpness_interpretation': self._interpret_sharpness_score(sharpness),
284                'severity': severity,
285                'severity_confidence': float(confidence),
286                'edge_density': float(edge_density),
287                'edge_density_interpretation': self._interpret_edge_density(edge_density),
288                
289                # Gradient and Frequency Analysis
290                'average_gradient': float(avg_gradient),
291                'max_gradient': float(max_gradient),
292                'gradient_interpretation': self._interpret_gradients(avg_gradient, max_gradient),
293                'high_frequency_content': float(high_freq_content),
294                'frequency_domain_analysis': self._interpret_frequency_content(high_freq_content),
295                
296                # Blur Type Analysis
297                'primary_type': blur_analysis['primary_type'],
298                'type_confidence': blur_analysis['confidence'],
299                'blur_reasoning': blur_analysis['reasoning'],
300                'secondary_issues': blur_analysis['secondary_issues'],
301                
302                # Motion Blur Specifics
303                'motion_angle': float(motion_angle),
304                'motion_length': int(motion_length),
305                'motion_interpretation': self._interpret_motion_blur(motion_angle, motion_length),
306                
307                # Defocus Analysis
308                'defocus_score': float(defocus_score),
309                'defocus_interpretation': self._interpret_defocus(defocus_score),
310                
311                # Noise and Quality
312                'noise_level': float(noise_level),
313                'noise_interpretation': self._interpret_noise_level(noise_level),
314                'contrast_measure': float(contrast_measure),
315                'dynamic_range': float(dynamic_range),
316                'texture_variance': float(texture_variance),
317                
318                # Enhancement Strategy
319                'enhancement_priority': enhancement_strategy['priority'],
320                'recommended_methods': enhancement_strategy['methods'],
321                'expected_improvement': enhancement_strategy['expected_improvement'],
322                'processing_difficulty': enhancement_strategy['difficulty'],
323                'detailed_recommendations': enhancement_strategy['detailed_recommendations'],
324                
325                # Technical Analysis Summary
326                'technical_summary': self._generate_technical_summary(
327                    sharpness, blur_analysis['primary_type'], severity, noise_level
328                ),
329                'student_analysis_notes': self._generate_student_notes(
330                    sharpness, motion_length, defocus_score, edge_density
331                )
332            }
333            
334        except Exception as e:
335            logger.error(f"Error in comprehensive analysis: {e}")
336            return {
337                'sharpness_score': 0.0,
338                'severity': 'Unknown',
339                'severity_confidence': 0.0,
340                'primary_type': 'Unknown',
341                'type_confidence': 0.0,
342                'motion_angle': 0.0,
343                'motion_length': 0,
344                'defocus_score': 0.0,
345                'noise_level': 0.0,
346                'enhancement_priority': 'High',
347                'technical_summary': 'Analysis failed due to processing error',
348                'student_analysis_notes': 'Unable to perform detailed analysis'
349            }
350
351    def _categorize_image_size(self, width: int, height: int) -> str:
352        """Categorize image size for processing complexity assessment"""
353        total_pixels = width * height
354        if total_pixels < 100000:  # < 0.1 MP
355            return "Small (Fast Processing)"
356        elif total_pixels < 1000000:  # < 1 MP
357            return "Medium (Standard Processing)"
358        elif total_pixels < 5000000:  # < 5 MP
359            return "Large (Slower Processing)"
360        else:
361            return "Very Large (Requires Optimization)"
362
363    def _interpret_sharpness_score(self, sharpness: float) -> str:
364        """Provide educational interpretation of sharpness score"""
365        if sharpness > 1000:
366            return f"Excellent sharpness ({sharpness:.1f}). Strong edge definition with high contrast transitions."
367        elif sharpness > 600:
368            return f"Good sharpness ({sharpness:.1f}). Adequate edge clarity for most applications."
369        elif sharpness > 300:
370            return f"Moderate blur ({sharpness:.1f}). Noticeable softness in edges and details."
371        elif sharpness > 100:
372            return f"Significant blur ({sharpness:.1f}). Substantial loss of fine details and edge clarity."
373        else:
374            return f"Severe blur ({sharpness:.1f}). Major degradation requiring advanced restoration techniques."
375
376    def _interpret_edge_density(self, edge_density: float) -> str:
377        """Interpret edge density measurements"""
378        if edge_density > 0.1:
379            return f"High edge density ({edge_density:.3f}) - Rich in structural details and textures"
380        elif edge_density > 0.05:
381            return f"Medium edge density ({edge_density:.3f}) - Moderate structural content"
382        elif edge_density > 0.02:
383            return f"Low edge density ({edge_density:.3f}) - Smooth regions dominate, limited fine details"
384        else:
385            return f"Very low edge density ({edge_density:.3f}) - Predominantly smooth surfaces or severe blur"
386
387    def _interpret_gradients(self, avg_gradient: float, max_gradient: float) -> str:
388        """Analyze gradient characteristics for sharpness assessment"""
389        gradient_ratio = max_gradient / (avg_gradient + 1e-6)
390        if gradient_ratio > 10 and avg_gradient > 20:
391            return f"Strong gradients detected (avg: {avg_gradient:.1f}, max: {max_gradient:.1f}) - Good edge definition"
392        elif gradient_ratio > 5:
393            return f"Moderate gradients (avg: {avg_gradient:.1f}, max: {max_gradient:.1f}) - Some edge preservation"
394        else:
395            return f"Weak gradients (avg: {avg_gradient:.1f}, max: {max_gradient:.1f}) - Poor edge definition, likely blurred"
396
397    def _interpret_frequency_content(self, high_freq: float) -> str:
398        """Analyze frequency domain characteristics"""
399        if high_freq > 5.0:
400            return f"Rich high-frequency content ({high_freq:.2f}) - Preserves fine details and textures"
401        elif high_freq > 3.0:
402            return f"Moderate high-frequency content ({high_freq:.2f}) - Some detail preservation"
403        elif high_freq > 2.0:
404            return f"Limited high-frequency content ({high_freq:.2f}) - Loss of fine details"
405        else:
406            return f"Poor high-frequency content ({high_freq:.2f}) - Significant detail loss, heavy blur"
407
408    def _detailed_blur_classification(self, sharpness: float, motion_length: int, 
409                                    defocus_score: float, edge_density: float,
410                                    avg_gradient: float, high_freq: float) -> Dict:
411        """Comprehensive blur type analysis with detailed reasoning"""
412        
413        # Evidence collection for each blur type
414        motion_evidence = []
415        defocus_evidence = []
416        noise_evidence = []
417        mixed_evidence = []
418        
419        # Motion blur indicators
420        if motion_length > 15:
421            motion_evidence.append(f"Strong directional blur detected (length: {motion_length}px)")
422        if avg_gradient < 15 and sharpness < 400:
423            motion_evidence.append("Gradient analysis suggests directional degradation")
424        
425        # Defocus blur indicators  
426        if defocus_score > 0.4:
427            defocus_evidence.append(f"High defocus characteristics (score: {defocus_score:.3f})")
428        if edge_density < 0.03 and high_freq < 3.0:
429            defocus_evidence.append("Uniform blur pattern across all frequencies")
430        
431        # Mixed blur indicators
432        if motion_length > 10 and defocus_score > 0.3:
433            mixed_evidence.append("Both motion and defocus characteristics present")
434        if sharpness < 200:
435            mixed_evidence.append("Severe degradation suggests multiple blur sources")
436        
437        # Determine primary classification
438        if len(motion_evidence) >= 2 and motion_length > 12:
439            primary_type = "Motion Blur"
440            confidence = 0.85 + min(0.1, motion_length / 100)
441            reasoning = f"Motion blur identified based on: {', '.join(motion_evidence)}"
442            secondary_issues = defocus_evidence + mixed_evidence
443            
444        elif len(defocus_evidence) >= 2 and defocus_score > 0.35:
445            primary_type = "Defocus Blur"  
446            confidence = 0.80 + min(0.15, defocus_score)
447            reasoning = f"Defocus blur identified based on: {', '.join(defocus_evidence)}"
448            secondary_issues = motion_evidence + mixed_evidence
449            
450        elif sharpness > 800:
451            primary_type = "Sharp Image"
452            confidence = 0.90
453            reasoning = "High sharpness metrics indicate well-focused image"
454            secondary_issues = []
455            
456        else:
457            primary_type = "Mixed/Complex Blur"
458            confidence = 0.65
459            reasoning = f"Complex blur pattern detected. Evidence includes: {', '.join(motion_evidence + defocus_evidence)}"
460            secondary_issues = ["Multiple degradation sources present", "Requires combined enhancement approach"]
461        
462        return {
463            'primary_type': primary_type,
464            'confidence': confidence,
465            'reasoning': reasoning,
466            'secondary_issues': secondary_issues if secondary_issues else ["No significant secondary issues detected"]
467        }
468
469    def _interpret_motion_blur(self, angle: float, length: int) -> str:
470        """Detailed motion blur parameter interpretation"""
471        if length < 5:
472            return f"Minimal motion (Length: {length}px) - Not significant for restoration"
473        elif length < 15:
474            return f"Moderate linear motion (Angle: {angle:.1f}°, Length: {length}px) - Correctable with standard techniques"
475        elif length < 30:
476            return f"Significant motion blur (Angle: {angle:.1f}°, Length: {length}px) - Requires advanced deconvolution"
477        else:
478            return f"Severe motion blur (Angle: {angle:.1f}°, Length: {length}px) - Challenging restoration case"
479
480    def _interpret_defocus(self, defocus_score: float) -> str:
481        """Interpret defocus blur characteristics"""
482        if defocus_score < 0.2:
483            return f"Minimal defocus ({defocus_score:.3f}) - Sharp focus maintained"
484        elif defocus_score < 0.4:
485            return f"Moderate defocus ({defocus_score:.3f}) - Some focus softness present"  
486        elif defocus_score < 0.6:
487            return f"Significant defocus ({defocus_score:.3f}) - Noticeable out-of-focus blur"
488        else:
489            return f"Severe defocus ({defocus_score:.3f}) - Major focus problems requiring restoration"
490
491    def _interpret_noise_level(self, noise_level: float) -> str:
492        """Analyze noise characteristics and impact"""
493        if noise_level < 0.1:
494            return f"Low noise ({noise_level:.3f}) - Clean image, minimal interference"
495        elif noise_level < 0.3:
496            return f"Moderate noise ({noise_level:.3f}) - Some grain present but manageable"
497        elif noise_level < 0.5:
498            return f"High noise ({noise_level:.3f}) - Significant grain affecting image quality"
499        else:
500            return f"Severe noise ({noise_level:.3f}) - Heavy noise requiring specialized filtering"
501
502    def _recommend_enhancement_strategy(self, blur_type: str, severity: str, 
503                                      noise_level: float, motion_length: int) -> Dict:
504        """Generate detailed enhancement recommendations"""
505        
506        if "Sharp" in blur_type:
507            return {
508                'priority': 'Low',
509                'methods': ['Optional sharpening enhancement'],
510                'expected_improvement': '5-10%',
511                'difficulty': 'Easy',
512                'detailed_recommendations': [
513                    "Image is already well-focused",
514                    "Consider mild unsharp masking if enhancement desired",
515                    "Focus on noise reduction if noise_level > 0.2"
516                ]
517            }
518        
519        elif "Motion" in blur_type:
520            methods = ['Wiener Filter', 'Richardson-Lucy Deconvolution']
521            if motion_length > 20:
522                methods.append('Advanced CNN Enhancement')
523            
524            difficulty = 'Medium' if motion_length < 20 else 'Hard'
525            improvement = '30-60%' if motion_length < 25 else '20-45%'
526            
527            recommendations = [
528                f"Apply motion deblurring with {motion_length}px kernel",
529                "Use Richardson-Lucy for best results with known PSF",
530                "Consider CNN enhancement for complex cases"
531            ]
532            
533            if noise_level > 0.3:
534                recommendations.append("Apply noise reduction before deblurring")
535            
536        elif "Defocus" in blur_type:
537            methods = ['Gaussian Deconvolution', 'Wiener Filter', 'CNN Enhancement']
538            difficulty = 'Medium'
539            improvement = '25-50%'
540            
541            recommendations = [
542                "Use Gaussian PSF estimation for deconvolution", 
543                "Apply iterative Richardson-Lucy algorithm",
544                "CNN methods often work well for defocus blur"
545            ]
546            
547        else:  # Mixed/Complex
548            methods = ['Combined Approach', 'CNN Enhancement', 'Multi-stage Processing']
549            difficulty = 'Hard'
550            improvement = '20-40%'
551            
552            recommendations = [
553                "Try multiple deblurring approaches sequentially",
554                "CNN enhancement recommended for complex cases",
555                "May require manual parameter tuning"
556            ]
557        
558        # Adjust for noise
559        if noise_level > 0.4:
560            recommendations.insert(0, "Critical: Apply aggressive noise reduction first")
561            improvement = improvement.replace('0%', '5%').replace('5%', '0%')  # Reduce expected improvement
562        
563        return {
564            'priority': 'High' if 'Severe' in severity else 'Medium',
565            'methods': methods,
566            'expected_improvement': improvement,
567            'difficulty': difficulty,
568            'detailed_recommendations': recommendations
569        }
570
571    def _generate_technical_summary(self, sharpness: float, blur_type: str, 
572                                  severity: str, noise_level: float) -> str:
573        """Generate comprehensive technical analysis summary"""
574        return f"""
575TECHNICAL ANALYSIS SUMMARY:
576• Sharpness Assessment: {severity} blur detected (Laplacian variance: {sharpness:.1f})
577• Primary Issue: {blur_type} identified as dominant degradation
578• Noise Characteristics: {'Low' if noise_level < 0.2 else 'High'} noise environment 
579• Processing Complexity: {'Standard' if sharpness > 300 else 'Advanced'} restoration required
580• Image Condition: {'Recoverable' if sharpness > 100 else 'Severely degraded'} with appropriate methods
581        """.strip()
582
583    def _generate_student_notes(self, sharpness: float, motion_length: int, 
584                              defocus_score: float, edge_density: float) -> str:
585        """Generate educational analysis notes"""
586        return f"""
587DETAILED ANALYSIS NOTES:
588📊 Quantitative Measurements:
589   - Variance of Laplacian (sharpness): {sharpness:.1f}
590   - Motion blur estimation: {motion_length}px kernel length
591   - Defocus blur score: {defocus_score:.3f} (0=sharp, 1=heavily defocused)
592   - Edge density ratio: {edge_density:.3f} (proportion of edge pixels)
593
594🔍 Image Processing Observations:
595   - {"Strong" if sharpness > 600 else "Weak"} high-frequency content preservation
596   - {"Directional" if motion_length > 10 else "Uniform"} blur pattern characteristics  
597   - {"Adequate" if edge_density > 0.05 else "Poor"} structural detail retention
598   - Enhancement difficulty: {"Low" if sharpness > 400 else "High"} (based on degradation severity)
599
600💡 Recommended Analysis Approach:
601   1. Frequency domain analysis confirms blur type identification
602   2. Gradient-based metrics support sharpness assessment  
603   3. PSF estimation required for optimal deconvolution
604   4. Multi-metric validation ensures robust classification
605        """.strip()
606
607def detect_blur_type(image: np.ndarray) -> str:
608    """
609    Simple blur type detection function
610    
611    Args:
612        image: Input image
613    
614    Returns:
615        str: Blur type ('sharp', 'motion', 'defocus', 'mixed')
616    """
617    detector = BlurDetector()
618    analysis = detector.comprehensive_analysis(image)
619    
620    blur_type = analysis['primary_type'].lower().replace(' ', '_')
621    return blur_type
622
623def get_sharpness_score(image: np.ndarray) -> float:
624    """
625    Get sharpness score for image
626    
627    Args:
628        image: Input image
629    
630    Returns:
631        float: Sharpness score (Laplacian variance)
632    """
633    detector = BlurDetector()
634    return detector.variance_of_laplacian(image)
635
636# Example usage and testing
637if __name__ == "__main__":
638    print("Blur Detection Module - Testing")
639    print("===============================")
640    
641    # Create test images
642    # Sharp test image
643    sharp_image = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
644    
645    # Blurred test image (simulated)
646    blurred_image = cv2.GaussianBlur(sharp_image, (15, 15), 5)
647    
648    # Initialize detector
649    detector = BlurDetector()
650    
651    # Test sharp image
652    print("\n--- Sharp Image Analysis ---")
653    sharp_analysis = detector.comprehensive_analysis(sharp_image)
654    for key, value in sharp_analysis.items():
655        print(f"{key}: {value}")
656    
657    # Test blurred image
658    print("\n--- Blurred Image Analysis ---")
659    blurred_analysis = detector.comprehensive_analysis(blurred_image)
660    for key, value in blurred_analysis.items():
661        print(f"{key}: {value}")
662    
663    print("\nBlur detection module test completed!")
664
665
666def analyze_blur_characteristics(image: np.ndarray) -> Dict:
667    """
668    Standalone function for blur analysis (for backward compatibility)
669    
670    Args:
671        image: Input image array
672        
673    Returns:
674        dict: Comprehensive blur analysis results
675    """
676    detector = BlurDetector()
677    return detector.comprehensive_analysis(image)
678
679
680if __name__ == "__main__":
681    test_blur_detection()