File size: 7,617 Bytes
6727dc4
252549e
 
 
a0b1614
252549e
3f4d43c
168adbc
9464db5
 
7a6994e
3f4d43c
12893c6
1623ecc
06c0907
7a6994e
 
 
 
 
 
97a0169
 
 
7a6994e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7dcbb03
3a13b8b
 
 
 
 
252549e
3a13b8b
7dcbb03
 
3a13b8b
7dcbb03
 
 
 
 
 
 
 
3a13b8b
 
7dcbb03
 
3a13b8b
 
7dcbb03
3a13b8b
252549e
 
 
3a13b8b
 
252549e
3a13b8b
 
 
252549e
3a13b8b
252549e
 
a0b1614
252549e
 
3a13b8b
12893c6
252549e
 
3a13b8b
252549e
 
 
3a13b8b
168adbc
252549e
6727dc4
 
3a13b8b
7dcbb03
 
6727dc4
 
3a13b8b
252549e
a0b1614
 
3a13b8b
a0b1614
 
9464db5
 
 
 
 
3a13b8b
9464db5
3a13b8b
388621a
 
 
 
 
 
 
3a13b8b
9464db5
3a13b8b
 
9464db5
3a13b8b
9464db5
 
3a13b8b
9464db5
f4759e7
9464db5
 
 
 
 
6727dc4
 
3a13b8b
 
12893c6
 
 
 
 
3a13b8b
168adbc
3a13b8b
9c6cf28
3a13b8b
3f4d43c
 
12893c6
9c6cf28
7dcbb03
 
 
3a13b8b
6727dc4
3a13b8b
6727dc4
 
7dcbb03
3a13b8b
7dcbb03
 
 
3a13b8b
 
9464db5
3a13b8b
 
9464db5
3a13b8b
6727dc4
3a13b8b
098c355
3a13b8b
7a6994e
3a13b8b
7dcbb03
3a13b8b
32d99a2
a0b1614
6727dc4
 
7a6994e
6727dc4
 
 
 
 
098c355
6727dc4
 
 
 
 
a0b1614
3a13b8b
12893c6
a0b1614
3a8eb59
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import gradio as gr
import cv2
import numpy as np
from fpdf import FPDF
import tempfile
import os
from paddleocr import PaddleOCR
import time
from simple_salesforce import Salesforce
from datetime import datetime
import boto3

# Initialize PaddleOCR once with updated parameters
ocr_model = PaddleOCR(use_textline_orientation=True, lang='en')

def upload_image_and_get_url(image_path):
    """
    Upload the image to AWS S3 and return the public URL.
    """
    s3_client = boto3.client(
        's3',
        aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],  # Set environment variable
        aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],  # Set environment variable
        region_name=os.environ['AWS_REGION']  # Set environment variable
    )

    # Define the S3 bucket name
    bucket_name = 'your-bucket-name'

    # Generate a unique key for the image (e.g., using the file name)
    image_key = f"images/{os.path.basename(image_path)}"

    # Upload the image to S3
    s3_client.upload_file(image_path, bucket_name, image_key)

    # Construct the public URL for the uploaded image
    image_url = f"https://{bucket_name}.s3.{os.environ['AWS_REGION']}.amazonaws.com/{image_key}"

    return image_url

def analyze_uv_coverage(img, brightness_threshold=150, kernel_size=5, apply_blur=True, adaptive_thresh=False):
    """
    Analyze UV sterilization coverage by thresholding the grayscale image.
    Optional adaptive thresholding and Gaussian blur for noise reduction.
    Morphological operations clean the mask for better accuracy.
    """
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    if apply_blur:
        gray = cv2.GaussianBlur(gray, (5, 5), 0)

    if adaptive_thresh:
        binary_mask = cv2.adaptiveThreshold(
            gray, 255,
            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY,
            11, 2)
    else:
        _, binary_mask = cv2.threshold(gray, brightness_threshold, 255, cv2.THRESH_BINARY)

    # Morphological opening (erosion followed by dilation) to remove noise
    kernel = np.ones((kernel_size, kernel_size), np.uint8)
    binary_mask = cv2.morphologyEx(binary_mask, cv2.MORPH_OPEN, kernel, iterations=1)

    # Morphological closing (dilation followed by erosion) to close small holes inside foreground
    binary_mask = cv2.morphologyEx(binary_mask, cv2.MORPH_CLOSE, kernel, iterations=1)

    total_pixels = binary_mask.size
    sterilized_pixels = cv2.countNonZero(binary_mask)
    coverage_percent = (sterilized_pixels / total_pixels) * 100

    # Create overlay for visualization: Green = sterilized, Red = unsterilized
    overlay = img.copy()
    overlay[binary_mask == 255] = [0, 255, 0]  # Green
    overlay[binary_mask == 0] = [0, 0, 255]    # Red

    annotated_img = cv2.addWeighted(img, 0.6, overlay, 0.4, 0)

    return annotated_img, coverage_percent

def create_pdf_report(coverage_percent, extracted_texts, annotated_image_path, output_path):
    pdf = FPDF()
    pdf.add_page()

    pdf.set_font("Arial", 'B', 16)
    pdf.cell(200, 10, txt="UV Sterilization Report", ln=True, align='C')
    pdf.ln(10)

    pdf.set_font("Arial", size=12)
    pdf.cell(0, 10, f"Sterilization Coverage: {coverage_percent:.2f}%", ln=True)
    pdf.ln(5)

    pdf.cell(0, 10, "Extracted Text from Image (OCR):", ln=True)
    pdf.set_font("Arial", size=10)
    if extracted_texts:
        for text in extracted_texts:
            # Filter out very short or empty OCR texts to improve clarity
            if len(text.strip()) > 1:
                pdf.multi_cell(0, 8, f"- {text}")
    else:
        pdf.cell(0, 8, "No text detected.", ln=True)

    pdf.ln(10)
    pdf.cell(0, 10, "Annotated Image:", ln=True)
    pdf.image(annotated_image_path, x=10, y=pdf.get_y(), w=pdf.w - 20)

    pdf.output(output_path)

def save_record_to_salesforce(annotated_image_url, coverage_percent, original_image_pil, compliance_threshold=80):
    sf = Salesforce(
        username=os.environ['SF_USERNAME'],
        password=os.environ['SF_PASSWORD'],
        security_token=os.environ['SF_SECURITY_TOKEN'],
        domain=os.environ.get('SF_DOMAIN', 'login')  # 'test' for sandbox
    )
    
    # Save original image temporarily, upload it, get URL
    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_orig_img_file:
        original_image_pil.save(temp_orig_img_file.name, format="JPEG")
        temp_orig_img_path = temp_orig_img_file.name
    
    original_image_url = upload_image_and_get_url(temp_orig_img_path)
    os.unlink(temp_orig_img_path)

    compliance_status = 'Pass' if coverage_percent >= compliance_threshold else 'Fail'
    technician_id = os.environ.get('SF_TECHNICIAN_ID')  # Salesforce UserId lookup
    
    record_name = f"UV Verification - {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}"

    sf.UV_Verification__c.create({
        'Name': record_name,
        'Annotated_Image__c': annotated_image_url,
        'Coverage_Percentage__c': round(coverage_percent, 2),
        'Original_Image__c': original_image_url,  # Correct field API name here
        'Compliance_Status__c': compliance_status,
        'Technician_ID__c': technician_id,
        'Verified_On__c': datetime.utcnow().isoformat()
    })

def process_image(input_img, brightness_threshold=150):
    img = cv2.cvtColor(np.array(input_img), cv2.COLOR_RGB2BGR)

    # Resize large images for faster processing, preserving aspect ratio
    max_dim = 640
    h, w = img.shape[:2]
    if max(h, w) > max_dim:
        scale = max_dim / max(h, w)
        img = cv2.resize(img, (int(w * scale), int(h * scale)))

    start_time = time.time()
    ocr_result = ocr_model.ocr(img)
    ocr_time = time.time() - start_time

    extracted_texts = []
    for line in ocr_result:
        if line:
            for word_info in line:
                text = word_info[1][0].strip()
                if len(text) > 1:
                    extracted_texts.append(text)

    annotated_img, coverage_percent = analyze_uv_coverage(img, brightness_threshold)

    with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_img_file:
        cv2.imwrite(temp_img_file.name, annotated_img)
        annotated_img_path = temp_img_file.name

    temp_pdf_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
    temp_pdf_file.close()
    create_pdf_report(coverage_percent, extracted_texts, annotated_img_path, temp_pdf_file.name)

    # Upload annotated image and get URL
    annotated_image_url = upload_image_and_get_url(annotated_img_path)

    # Save record in Salesforce
    save_record_to_salesforce(annotated_image_url, coverage_percent, input_img)

    annotated_img_rgb = cv2.cvtColor(annotated_img, cv2.COLOR_BGR2RGB)

    report_text = f"UV Sterilization Coverage: {coverage_percent:.2f}%"


    # Clean up temp image file after PDF generation
    os.unlink(annotated_img_path)

    return annotated_img_rgb, report_text, temp_pdf_file.name

iface = gr.Interface(
    fn=process_image,
    inputs=[ 
        gr.Image(type="pil", label="Upload Post-UV Sterilization Image"),
        gr.Slider(50, 255, value=150, step=1, label="Brightness Threshold")
    ],
    outputs=[
        gr.Image(type="numpy", label="Annotated Image"),
        gr.Textbox(label="UV Sterilization Report", lines=5),
        gr.File(label="Download PDF Report")
    ],
    title="UV Sterilization Coverage Analyzer",
    description="Upload a post-UV sterilization image to analyze surface coverage and generate a compliance report."
)

iface.queue()  # Enable request queuing to improve UX on heavy processing

if __name__ == "__main__":
    iface.launch()