
    jj                        d Z ddlmZ ddlZddlmZ ddlmZ ddlZddl	Z
ddlmZ  ed       G d	 d
             ZddZddZddZddZy)u  Image preprocessing for EKG strips.

For PTB-XL rendered images this is essentially a no-op — the renderer
already produces clean grids on a parchment background. For user-uploaded
phone photos, the pipeline below trims margins, corrects light skew, and
normalizes contrast so the multimodal model sees the cleanest possible
representation.

Each step is conservative: skip if the input looks already-good. The cost
of doing nothing on a clean image is small; the cost of mangling a clean
image with aggressive deskew is large.
    )annotationsN)	dataclass)Path)ImageT)frozenc                  >    e Zd ZU ded<   ded<   ded<   ded<   d
dZy	)PreparedImagezImage.Imagepilintwidthheightz	list[str]notesc                    t        j                         }| j                  j                  |dd       |j	                         S )NPNGT)formatoptimize)ioBytesIOr
   savegetvalue)selfbufs     L/var/www/adampowell.pro/apps/ekg-tutor/interpret/src/pipelines/image_prep.pyto_png_byteszPreparedImage.to_png_bytes    s/    jjlc%$7||~    N)returnbytes)__name__
__module____qualname____annotations__r    r   r   r	   r	      s    	JKr   r	   c                ^   t        |       }|j                         st        |      t        j                  |      j                  d      }g }t        j                  |      }t        j                  |t        j                        }t        |      \  }}|r|j                  d       t        |      \  }}|r|j                  d       t        |      }t        j                  |t        j                        }t        j                   |      }t#        ||j$                  |j&                  |      S )zLoad an image from disk and return a preprocessed copy.

    The original is never modified. Notes accumulate for any operation
    that was applied so the upstream service can attach them to
    image_quality_notes on the canonical interpretation.
    RGBzauto-cropped uniform borderszrotated to correct skew)r
   r   r   r   )r   existsFileNotFoundErrorr   openconvertnparraycv2cvtColorCOLOR_RGB2BGR_autocrop_bordersappend_maybe_deskew_normalize_contrastCOLOR_BGR2RGB	fromarrayr	   r   r   )	pathpr
   r   np_imgbgrappliedrgbouts	            r   preparer;   &   s     	T
A88:""
**Q-


&CEXXc]F
,,vs00
1C$S)LC34 %LC./
c
"C
,,sC--
.C
//#
CS		#**ERRr   c                   t        j                  | t         j                        }|dkD  |dk  z  j                  t        j
                        dz  }t        j                  |      }t        j                  |      }|| dfS t        j                  |      \  }}}}| j                  dd \  }	}
|dk  r|dk  r||z   |
dz
  kD  r||z   |	dz
  kD  r| dfS |d|
z  k  s|d|	z  k  r| dfS | |||z   |||z   f   d	fS )
zfTrim solid-color borders. Conservative: bails out if it would crop
    more than 12% in any direction.         NF      g)\(?T)
r+   r,   COLOR_BGR2GRAYastyper)   uint8bitwise_notfindNonZeroboundingRectshape)r7   graymaskinvcoordsxywhHWs              r   r.   r.   F   s    <<S//0DCZD2I&..rxx83>D
//$
C__S!F~Ez!!&)JAq!Q99Ra=DAq1uQAEQU?AQEz4!8|q4!8|Ezq1q5y!a!e)#$d**r   c                   t        j                  | t         j                        }t        j                  |ddd      }t        j                  |dt
        j                  dz  ddd	      }|| dfS g }|d
d D ]e  }|d   \  }}}}	||z
  }
|	|z
  }|
dk(  rt        j                  t        j                  ||
            }d|cxk  rdk  sRn U|j                  |       g t        |      dk  r| dfS t        t        j                  |            }t        |      dk  r| dfS | j                  d
d \  }}t        j                  |dz  |dz  f|d      }t        j                   | |||ft         j"                  t         j$                        }|dfS )zrDetect dominant near-horizontal lines (EKG grid) and rotate to align.
    Falls back to identity on any ambiguity.2         )apertureSize      x   
   )	thresholdminLineLength
maxLineGapNF   r   i   g      ?r@   g      ?)flags
borderModeT)r+   r,   rB   CannyHoughLinesPr)   pidegreesarctan2r/   lenfloatmedianabsrH   getRotationMatrix2D
warpAffineINTER_CUBICBORDER_REPLICATE)r7   rI   edgeslinesangleslinex1y1x2y2dxdyangrj   rQ   rR   matrixrotateds                     r   r0   r0   Z   s    <<S//0DIIdB!4EOOq"%%#+CBE }EzFdsaBB"W"W7jjB+,>r>MM#  6{REz299V$%F
6{SEz99Ra=DAq$$a!eQU^VSAFnn	
Aoo''G D=r   c                @   t        j                  | t         j                        }t        j                  |      \  }}}t        j                  dd      }|j                  |      }t        j                  t        j                  |||g      t         j                        S )zqMild CLAHE on the L channel of LAB. Conservative clip limit so the
    parchment-on-grid look stays recognizable.g      ?)   r~   )	clipLimittileGridSize)r+   r,   COLOR_BGR2LABsplitcreateCLAHEapplymergeCOLOR_LAB2BGR)r7   lablabclahel2s          r   r1   r1      sp     ,,sC--
.CiinGAq!OOc?E	QB<<		2q!*-s/@/@AAr   )r4   z
str | Pathr   r	   )r7   
np.ndarrayr   ztuple[np.ndarray, bool])r7   r   r   r   )__doc__
__future__r   r   dataclassesr   pathlibr   r+   numpyr)   PILr   r	   r;   r.   r0   r1   r"   r   r   <module>r      sW    # 	 !  
   $	 	 	S@+("JBr   