Spaces:
Sleeping
Sleeping
| from reportlab.pdfgen import canvas | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.lib.utils import ImageReader | |
| from io import BytesIO | |
| from models.schemas import MeasurementMetadata, MeasurementResult | |
| from PIL import Image, ImageDraw | |
| import logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class PdfService: | |
| """ | |
| A service to generate PDF reports. | |
| """ | |
| def create_report( | |
| self, | |
| image_bytes: bytes, | |
| metadata: MeasurementMetadata, | |
| results: MeasurementResult, | |
| ml_results: dict | |
| ) -> bytes: | |
| """ | |
| Generates a PDF report with the measurement data. | |
| """ | |
| logger.info(f"Creating PDF with metadata: {metadata}") | |
| logger.info(f"Creating PDF with results: {results}") | |
| buffer = BytesIO() | |
| p = canvas.Canvas(buffer, pagesize=letter) | |
| width, height = letter | |
| # Title | |
| p.setFont("Helvetica-Bold", 16) | |
| p.drawString(72, height - 72, "Ship Draft Measurement Report") | |
| # Metadata | |
| p.setFont("Helvetica", 12) | |
| p.drawString(72, height - 108, f"Ship ID: {metadata.ship_id}") | |
| p.drawString(72, height - 126, f"Timestamp: {metadata.timestamp.strftime('%Y-%m-%d %H:%M:%S')}") | |
| p.drawString(72, height - 144, f"Latitude: {metadata.latitude}") | |
| p.drawString(72, height - 162, f"Longitude: {metadata.longitude}") | |
| # # Measurement Results | |
| # p.setFont("Helvetica-Bold", 14) | |
| # p.drawString(72, height - 198, "Measurement Results") | |
| # p.setFont("Helvetica", 12) | |
| # p.drawString(90, height - 218, f"Draft Measurement: {results.draft_measurement:.1f} meters") | |
| # p.drawString(90, height - 236, f"Confidence Score: {results.confidence_score:.2%}") | |
| # ML Results | |
| p.setFont("Helvetica-Bold", 14) | |
| y_position = height - 270 | |
| p.drawString(72, y_position, "ML Results") | |
| y_position -= 18 | |
| p.setFont("Helvetica", 12) | |
| # Highlight draft from ML results | |
| if 'draft' in ml_results: | |
| p.setFont("Helvetica-Bold", 12) # Highlight | |
| p.drawString(90, y_position, f"ML Draft: {ml_results['draft']:.2f} meters") | |
| p.setFont("Helvetica", 12) # Reset font | |
| y_position -= 18 | |
| # Add other ML results (excluding arrays and images) | |
| for key, value in ml_results.items(): | |
| if key in ['pose_results', 'segment_results', 'original_image', 'pose_image_result', 'segment_image_result', 'final_image_result']: | |
| continue | |
| p.drawString(90, y_position, f"{key.replace('_', ' ').title()}: {value}") | |
| y_position -= 18 | |
| # Images | |
| p.setFont("Helvetica-Bold", 14) | |
| y_position -= 18 | |
| p.drawString(72, y_position, "Images") | |
| y_position -= 18 | |
| p.setFont("Helvetica", 12) | |
| # Convert numpy arrays to PIL Image and then to bytes for ReportLab | |
| def get_image_bytes(np_array): | |
| if np_array is None: | |
| return None | |
| img = Image.fromarray(np_array.astype('uint8')) | |
| img_byte_arr = BytesIO() | |
| img.save(img_byte_arr, format='PNG') | |
| return img_byte_arr.getvalue() | |
| # Define starting positions and dimensions for horizontal layout | |
| x_start = 72 | |
| image_width = 150 | |
| image_height = 150 # Assuming square or adjust as needed | |
| y_image_row = y_position - image_height - 20 # Position for the bottom of the images | |
| current_x = x_start | |
| # Draw original image | |
| if 'original_image' in ml_results and ml_results['original_image'] is not None: | |
| original_img_bytes = get_image_bytes(ml_results['original_image']) | |
| if original_img_bytes: | |
| p.drawString(current_x, y_image_row + image_height + 5, "Original Image:") # Label above image | |
| p.drawImage(ImageReader(BytesIO(original_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True) | |
| current_x += image_width + 20 # Move x for next image | |
| # Draw pose image result | |
| if 'pose_image_result' in ml_results and ml_results['pose_image_result'] is not None: | |
| pose_img_bytes = get_image_bytes(ml_results['pose_image_result']) | |
| if pose_img_bytes: | |
| p.drawString(current_x, y_image_row + image_height + 5, "Pose Image Result:") | |
| p.drawImage(ImageReader(BytesIO(pose_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True) | |
| current_x += image_width + 20 | |
| # Draw segment image result | |
| if 'segment_image_result' in ml_results and ml_results['segment_image_result'] is not None: | |
| segment_img_bytes = get_image_bytes(ml_results['segment_image_result']) | |
| if segment_img_bytes: | |
| p.drawString(current_x, y_image_row + image_height + 5, "Segment Image Result:") | |
| p.drawImage(ImageReader(BytesIO(segment_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True) | |
| # No need to update current_x as it's the last image in the row | |
| # Update y_position for content after images | |
| y_position = y_image_row - 20 # Adjust y_position to be below the images | |
| p.showPage() | |
| p.save() | |
| pdf_bytes = buffer.getvalue() | |
| buffer.close() | |
| return pdf_bytes | |