eho69 commited on
Commit
a3c4a9e
Β·
verified Β·
1 Parent(s): 32ffff1

Create batch.py

Browse files
Files changed (1) hide show
  1. batch.py +246 -0
batch.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Batch Processing Script for Engine Scanning System
3
+ Process multiple engine images in a directory
4
+ """
5
+
6
+ import cv2
7
+ import os
8
+ from pathlib import Path
9
+ import json
10
+ from datetime import datetime
11
+ import argparse
12
+ from app import EngineScanner
13
+ from tqdm import tqdm
14
+
15
+ class BatchProcessor:
16
+ """
17
+ Batch processing for multiple engine images
18
+ """
19
+
20
+ def __init__(self, input_dir, output_dir=None):
21
+ self.scanner = EngineScanner()
22
+ self.input_dir = Path(input_dir)
23
+
24
+ if output_dir is None:
25
+ self.output_dir = Path("batch_results")
26
+ else:
27
+ self.output_dir = Path(output_dir)
28
+
29
+ self.output_dir.mkdir(exist_ok=True)
30
+
31
+ # Supported image formats
32
+ self.image_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
33
+
34
+ def get_image_files(self):
35
+ """Get all image files from input directory"""
36
+ image_files = []
37
+ for ext in self.image_extensions:
38
+ image_files.extend(self.input_dir.glob(f'*{ext}'))
39
+ image_files.extend(self.input_dir.glob(f'*{ext.upper()}'))
40
+ return sorted(image_files)
41
+
42
+ def process_batch(self, save_images=True, generate_report=True):
43
+ """Process all images in the input directory"""
44
+ image_files = self.get_image_files()
45
+
46
+ if not image_files:
47
+ print(f"No images found in {self.input_dir}")
48
+ return
49
+
50
+ print(f"Found {len(image_files)} images to process")
51
+ print(f"Output directory: {self.output_dir}")
52
+ print()
53
+
54
+ results = []
55
+ stats = {
56
+ 'total': len(image_files),
57
+ 'pass': 0,
58
+ 'warning': 0,
59
+ 'fail': 0,
60
+ 'error': 0
61
+ }
62
+
63
+ # Process each image
64
+ for img_file in tqdm(image_files, desc="Processing engines"):
65
+ try:
66
+ # Read image
67
+ image = cv2.imread(str(img_file))
68
+
69
+ if image is None:
70
+ print(f"Error reading {img_file.name}")
71
+ stats['error'] += 1
72
+ continue
73
+
74
+ # Scan engine
75
+ result_image, report = self.scanner.scan_engine(image)
76
+
77
+ # Extract status from the last scan
78
+ if self.scanner.scan_history:
79
+ last_scan = self.scanner.scan_history[-1]
80
+ status = last_scan['defect_analysis']['status']
81
+ stats[status.lower()] += 1
82
+
83
+ # Save to results
84
+ result_data = {
85
+ 'filename': img_file.name,
86
+ 'status': status,
87
+ 'timestamp': last_scan['timestamp'],
88
+ 'cylinders': last_scan['cylinders'],
89
+ 'defects': last_scan['defect_analysis']
90
+ }
91
+ results.append(result_data)
92
+
93
+ # Save annotated image if requested
94
+ if save_images and result_image is not None:
95
+ output_path = self.output_dir / f"annotated_{img_file.name}"
96
+ cv2.imwrite(str(output_path), result_image)
97
+
98
+ except Exception as e:
99
+ print(f"Error processing {img_file.name}: {str(e)}")
100
+ stats['error'] += 1
101
+
102
+ # Generate batch report
103
+ if generate_report:
104
+ self.generate_batch_report(results, stats)
105
+
106
+ return results, stats
107
+
108
+ def generate_batch_report(self, results, stats):
109
+ """Generate comprehensive batch processing report"""
110
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
111
+
112
+ # Save JSON report
113
+ json_path = self.output_dir / f"batch_report_{timestamp}.json"
114
+ report_data = {
115
+ 'timestamp': timestamp,
116
+ 'statistics': stats,
117
+ 'results': results
118
+ }
119
+
120
+ with open(json_path, 'w') as f:
121
+ json.dump(report_data, f, indent=2)
122
+
123
+ # Generate text report
124
+ txt_path = self.output_dir / f"batch_report_{timestamp}.txt"
125
+
126
+ with open(txt_path, 'w') as f:
127
+ f.write("="*70 + "\n")
128
+ f.write("BATCH PROCESSING REPORT - ENGINE SCANNING SYSTEM\n")
129
+ f.write("="*70 + "\n\n")
130
+
131
+ f.write(f"Processing Date: {timestamp}\n")
132
+ f.write(f"Input Directory: {self.input_dir}\n")
133
+ f.write(f"Output Directory: {self.output_dir}\n\n")
134
+
135
+ f.write("-"*70 + "\n")
136
+ f.write("SUMMARY STATISTICS\n")
137
+ f.write("-"*70 + "\n")
138
+ f.write(f"Total Images Processed: {stats['total']}\n")
139
+ f.write(f" βœ“ PASS: {stats['pass']:3d} ({stats['pass']/stats['total']*100:.1f}%)\n")
140
+ f.write(f" ⚠ WARNING: {stats['warning']:3d} ({stats['warning']/stats['total']*100:.1f}%)\n")
141
+ f.write(f" βœ— FAIL: {stats['fail']:3d} ({stats['fail']/stats['total']*100:.1f}%)\n")
142
+ f.write(f" ! ERROR: {stats['error']:3d} ({stats['error']/stats['total']*100:.1f}%)\n\n")
143
+
144
+ f.write("-"*70 + "\n")
145
+ f.write("DETAILED RESULTS\n")
146
+ f.write("-"*70 + "\n\n")
147
+
148
+ # Sort by status (FAIL first, then WARNING, then PASS)
149
+ status_order = {'FAIL': 0, 'WARNING': 1, 'PASS': 2}
150
+ sorted_results = sorted(results, key=lambda x: status_order.get(x['status'], 3))
151
+
152
+ for result in sorted_results:
153
+ status_symbol = {
154
+ 'PASS': 'βœ“',
155
+ 'WARNING': '⚠',
156
+ 'FAIL': 'βœ—'
157
+ }.get(result['status'], '?')
158
+
159
+ f.write(f"{status_symbol} {result['status']:8s} | {result['filename']}\n")
160
+ f.write(f" Cylinders: {result['cylinders']}\n")
161
+ f.write(f" Defects: {result['defects']['defect_count']} "
162
+ f"({result['defects']['defect_percentage']:.2f}%)\n")
163
+ f.write("\n")
164
+
165
+ f.write("="*70 + "\n")
166
+ f.write("END OF REPORT\n")
167
+ f.write("="*70 + "\n")
168
+
169
+ print(f"\nβœ“ Batch report saved:")
170
+ print(f" - JSON: {json_path}")
171
+ print(f" - Text: {txt_path}")
172
+
173
+ # Print summary to console
174
+ print("\n" + "="*70)
175
+ print("BATCH PROCESSING COMPLETE")
176
+ print("="*70)
177
+ print(f"Total: {stats['total']}")
178
+ print(f"βœ“ PASS: {stats['pass']:3d} ({stats['pass']/stats['total']*100:.1f}%)")
179
+ print(f"⚠ WARNING: {stats['warning']:3d} ({stats['warning']/stats['total']*100:.1f}%)")
180
+ print(f"βœ— FAIL: {stats['fail']:3d} ({stats['fail']/stats['total']*100:.1f}%)")
181
+ print(f"! ERROR: {stats['error']:3d} ({stats['error']/stats['total']*100:.1f}%)")
182
+ print("="*70 + "\n")
183
+
184
+ def main():
185
+ parser = argparse.ArgumentParser(
186
+ description='Batch process engine images for quality control',
187
+ formatter_class=argparse.RawDescriptionHelpFormatter,
188
+ epilog="""
189
+ Examples:
190
+ # Process all images in a directory
191
+ python batch_process.py input_images/
192
+
193
+ # Process with custom output directory
194
+ python batch_process.py input_images/ -o results/
195
+
196
+ # Process without saving annotated images (faster)
197
+ python batch_process.py input_images/ --no-save-images
198
+
199
+ # Process without generating report (save time)
200
+ python batch_process.py input_images/ --no-report
201
+ """
202
+ )
203
+
204
+ parser.add_argument(
205
+ 'input_dir',
206
+ help='Directory containing engine images to process'
207
+ )
208
+
209
+ parser.add_argument(
210
+ '-o', '--output-dir',
211
+ help='Output directory for results (default: batch_results/)',
212
+ default=None
213
+ )
214
+
215
+ parser.add_argument(
216
+ '--no-save-images',
217
+ action='store_true',
218
+ help='Do not save annotated images (only generate report)'
219
+ )
220
+
221
+ parser.add_argument(
222
+ '--no-report',
223
+ action='store_true',
224
+ help='Do not generate batch report'
225
+ )
226
+
227
+ args = parser.parse_args()
228
+
229
+ # Validate input directory
230
+ if not os.path.isdir(args.input_dir):
231
+ print(f"Error: Input directory '{args.input_dir}' does not exist")
232
+ return 1
233
+
234
+ # Create processor
235
+ processor = BatchProcessor(args.input_dir, args.output_dir)
236
+
237
+ # Process batch
238
+ results, stats = processor.process_batch(
239
+ save_images=not args.no_save_images,
240
+ generate_report=not args.no_report
241
+ )
242
+
243
+ return 0
244
+
245
+ if __name__ == "__main__":
246
+ exit(main())