ganeshkumar383/AI-Based-Image-Deblurring-App
0
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()