File size: 3,149 Bytes
9cc92da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Web demo (Gradio): upload a photo, press Rectify, get the flat sheet and the status.

    pip install gradio && python3 app.py
"""

import tempfile
import uuid
from pathlib import Path

import cv2
import gradio as gr

from ecglib import illum, nets
from rectify import OUTPUT_PX_PER_MM, THREADS, rectify_photo

MAX_SIDE = 2048
MODELS = nets.load_models(threads=THREADS)
OUT = Path(tempfile.mkdtemp())


def status_text(report, shrunk):
    lines = ["**Status: {}** — {}".format(report.get("status"), report.get("reason"))]
    holdout = report.get("selfcheck_holdout_mm", {}).get("median_mm")
    if holdout is not None:
        lines.append("Self-check median: {} mm".format(holdout))
    if report.get("photo_px_per_mm"):
        lines.append("Photo scale: {:.2f} px/mm".format(report["photo_px_per_mm"]))
    if report.get("sheet_size_mm"):
        lines.append("Sheet: {:.0f} x {:.0f} mm".format(*report["sheet_size_mm"]))
    if shrunk:
        lines.append("Photo resized to {} px on the longer side".format(MAX_SIDE))
    lines.append("Time: {} s".format(report.get("times", {}).get("total")))
    return "\n\n".join(lines)


def run(path, fix_illumination):
    if path is None:
        return None, None, "Upload a photo first."
    photo = cv2.imread(path, cv2.IMREAD_COLOR)
    if photo is None:
        return None, None, "Cannot read the image."
    scale = MAX_SIDE / max(photo.shape[:2])
    if scale < 1:
        photo = cv2.resize(photo, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
    try:
        flat, check, report = rectify_photo(MODELS, photo)
    except Exception as error:
        return None, None, "**Status: fail** — {!r}".format(error)
    stem = OUT / uuid.uuid4().hex
    flat_path = check_path = None
    if flat is not None:
        if fix_illumination:
            flat = illum.flatten(flat, OUTPUT_PX_PER_MM, paper="white")
        flat_path = str(stem) + "-flat.jpg"
        cv2.imwrite(flat_path, flat, [cv2.IMWRITE_JPEG_QUALITY, 92])
    if check is not None:
        check_path = str(stem) + "-check.jpg"
        cv2.imwrite(check_path, check, [cv2.IMWRITE_JPEG_QUALITY, 85])
    return flat_path, check_path, status_text(report, scale < 1)


with gr.Blocks(title="ECG Rectifier") as demo:
    gr.Markdown("# ECG Rectifier\nA phone photo of a paper ECG (bent, crumpled, folded) -> a flat sheet "
                "with a square grid at 10 px/mm. CPU only; one photo takes about a minute.")
    with gr.Row():
        with gr.Column():
            photo = gr.Image(type="filepath", label="Photo")
            fix = gr.Checkbox(value=True, label="Fix illumination")
            button = gr.Button("Rectify", variant="primary")
            gr.Examples([["examples/folded.jpg"], ["examples/crumpled.jpg"]], inputs=[photo])
        with gr.Column():
            flat = gr.Image(type="filepath", label="Flat sheet")
            status = gr.Markdown()
            check = gr.Image(type="filepath", label="Mask (blue), working area (yellow), crop (green), grid nodes used (red)")
    button.click(run, inputs=[photo, fix], outputs=[flat, check, status])

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