File size: 6,096 Bytes
6a6918c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146f4af
 
 
 
 
 
 
 
 
6a6918c
146f4af
6a6918c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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

        if 'extracted_mark_image' in ml_results and ml_results['extracted_mark_image'] is not None:
            original_img_bytes = get_image_bytes(ml_results['extracted_mark_image'])
            if original_img_bytes:
                p.drawString(current_x, y_image_row + image_height + 5, "Extracted Mark 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