ó
    ÛÉj{   ã            
       ó6  • S r SSKrSSKrSSKJr  SSKJr  SSKrSSKr	\R                  " \5      r\R                  " 5       rSq\R                   " S5      rS\S\\   4S	 jrSS
SS\\   SS4S jjrSSS\\   4S jrSS jr  SS\\   SSS\S\\   4S jjrg)u  
qr_detect.py â€” lectura de cÃ³digos de familia en una imagen (cascada de hec-photo-app).

La usan el organizer (fotos que llegan por FTP) y el escaneo de fotogramas de la cÃ¡mara
(`scan_service`), para que los dos lean igual de bien. Cascada, de barata a cara:

  0. Puerta: Â¿hay un patrÃ³n QR visible? Se consultan los DOS detectores de OpenCV porque
     ninguno basta solo (medido sobre 250 tarjetas reales fotografiadas a tres distancias):

                            primer plano   media    lejos
         detect()               96,7 %     93,3 %   15,0 %   â†� falla el QR pequeÃ±o
         detectMulti()          25,0 %     93,3 %   96,7 %   â†� falla el primer plano
         detect | detectMulti   98,3 %     98,3 %   98,3 %

     El 90 % de las fotos son retratos sin QR y salen aquÃ­ sin mÃ¡s coste.
  1. DescodificaciÃ³n mÃºltiple (varias tarjetas en la foto; tolera el marco redondeado de la tarjeta).
  2. Gris tal cual.   3. CLAHE (reflejos / luz desigual).   4. Otsu (desenfoque / ruido).
  5. Enfoque (unsharp).
  6. OCR del cÃ³digo IMPRESO bajo el QR (tarjeta rayada o doblada). Solo con prefijos conocidos.

âš ï¸� El OCR lee el prefijo del colegio, no uno fijo: es el paso mÃ¡s caro (tesseract sobre ~12 Mpx)
y con un prefijo equivocado se pagarÃ­a entero para no encontrar nunca nada. Los prefijos salen
de la configuraciÃ³n del colegio MÃ�S los que ya estÃ©n en disco (la config y las tarjetas ya
repartidas no siempre coinciden). Si tesseract no estÃ¡ instalado, el primer fallo desactiva el
paso para siempre: no se reintenta foto a foto.
é    N)ÚPath)ÚOptionalFz^[A-Z0-9][A-Z0-9-]{1,23}$ÚrawÚreturnc                 óà  • U =(       d    SR                  5       nU(       d  gUR                  5       R                  S5      (       al  [        R                  " SU[        R
                  5      =(       d&    [        R                  " SU[        R
                  5      nU(       d  gUR                  S5      nUR                  SS5      R                  5       n[        R                  U5      (       a  U$ S$ )uB  Contenido de un QR â†’ cÃ³digo de familia, o None si no es un cÃ³digo vÃ¡lido.

Acepta el cÃ³digo a secas, una URL `â€¦/r/CÃ“DIGO` o `â€¦?code=CÃ“DIGO`. La validaciÃ³n de formato no es
cosmÃ©tica: el cÃ³digo acaba siendo el nombre de una carpeta, asÃ­ que un QR malicioso con `../../x`
no puede escribir fuera de photos/.
Ú N)zhttp://zhttps://z/r/([^/?#\s]+)z[?&]code=([^&#\s]+)é   Ú )ÚstripÚlowerÚ
startswithÚreÚsearchÚ
IGNORECASEÚgroupÚreplaceÚupperÚCODE_REÚmatch)r   ÚsÚmÚcodes       ÚUD:\Ingenieria\TRABAJOS ONLINE\Projects\11- Photo School\backend\services\qr_detect.pyÚextract_coder   ,   s«   € ð 
��×ÑÓ€AÞØØ‡w�wƒy×ÑÐ3×4Ñ4Ü�IŠIÐ'¨¬B¯M©MÓ:×q¼b¿iºiÐH^Ð`aÔce×cpÑcpÓ>qˆÞØØ�G‰G�A‹JˆØ�9‰9�S˜"Ó×#Ñ#Ó%€DÜ—=‘= ×&Ñ&ˆ4Ð0¨DÐ0ó    Ú
configuredzstr | set[str] | NoneÚ
output_dirzset[str]c                 ó  • [        5       n[        U [        5      (       a'  U (       a   UR                  U R	                  5       5        O2U (       a+  X  Vs1 s H  o3(       d  M  UR	                  5       iM     sn-  nU(       a÷   UR                  5        Hà  nUR                  5       (       a   UR                  R                  S5      (       a  M:  UR                  5        H’  nUR                  5       (       a   UR                  R                  S5      (       a  M:  [        R                  " SUR                  5      nU(       d  Md  UR                  UR                  S5      R	                  5       5        M”     Mâ     U$ U$ s  snf ! [         a     U$ f = f)ud   Prefijos de cÃ³digo en circulaciÃ³n: los configurados + los de las carpetas que ya existen en disco.)Ú.Ú_z^([A-Za-z]{2,6})[-0-9]r	   )ÚsetÚ
isinstanceÚstrÚaddr   ÚiterdirÚis_dirÚnamer   r   r   r   ÚOSError)r   r   ÚprefixesÚcÚphotographerÚfolderr   s          r   Úvalid_prefixesr-   ?   s+  € ä›€HÜ�*œc×"Ñ"¦zØ�‰�Z×%Ñ%Ó'Õ(Þ	Ø¨
Ó8ª
 1³a“Y�Q—W‘W–Y©
Ñ8Ñ8ˆÞð	Ø *× 2Ñ 2Ö 4�Ø#×*Ñ*×,Ñ,°×0AÑ0A×0LÑ0LÈZ×0XÑ0XÙØ*×2Ñ2Ö4�FØ!Ÿ=™=Ÿ?™?¨f¯k©k×.DÑ.DÀZ×.PÑ.PÙ ÜŸšÐ!:¸F¿K¹KÓH�Aß�qØ Ÿ™ Q§W¡W¨Q£Z×%5Ñ%5Ó%7Ö8ó 5ñ !5ð €Oˆ8€Oùò 9øô ó 	ØØ€Oð	ús%   Á
E9Á E9ÂB:E> Ä?6E> Å>
FÆFr)   c                 ó°  • [         (       d  U(       d  g SSKn [        U [        [        45      (       a.  [        R                  " [        U 5      [        R                  5      OU nUc  g[        UR                  5      S:X  a%  [        R                  " U[        R                  5      OUnUR                  S   S:  aK  [        SS[        UR                  S   S5      -  5      n[        R                   " USXU[        R"                  S9n[        R$                  " USS	[        R&                  [        R(                  -   5      u  pgUR+                  US
S9nSR-                  [/        [1        [2        R4                  U5      [        SS95      n	[2        R6                  " SU	 S3U[2        R8                  5      n
U
(       aˆ  U
R;                  S5      R=                  5       U
R;                  S5      R=                  5       U
R;                  S5      R=                  5       pÜnXË:w  a'  XÛ:w  a"  U SU SU 3n[        R	                  SU5        U$ [2        R6                  " SU	 S3U[2        R8                  5      n
U
(       a[  U
R;                  S5      R=                  5        U
R;                  S5       U
R;                  S5       3n[        R	                  SU5        U$  g! [         a    Sq [        R	                  S5         gf = f! [>         aV  nUR@                  RB                  S:X  a  Sq [        RE                  S5         SnAg[        RG                  SU5         SnAgSnAff = f)u`   Lee el cÃ³digo impreso (`PREFIJO-XXX-XXX`) con pytesseract. None si no hay prefijos / tesseract.Nr   TzFOCR de rescate no disponible (falta pytesseract): se omite en adelanteé   r	   i°  )ÚfxÚfyÚinterpolationéÿ   z--psm 11 --oem 3)ÚconfigÚ|)ÚkeyÚreversez\b(z')[-\s]([A-Z0-9]{3})[-\s]([A-Z0-9]{3})\bé   Ú-u   OCR detectÃ³ cÃ³digo: %sz)\s*(\d{2})\s*(\d{3})\bu,   OCR detectÃ³ cÃ³digo (formato numÃ©rico): %sÚTesseractNotFoundErroruC   OCR de rescate desactivado: tesseract no estÃ¡ instalado en el PATHzOCR error: %s)$Ú_ocr_unavailableÚpytesseractÚImportErrorÚloggerÚinfor"   r#   r   Úcv2ÚimreadÚIMREAD_COLORÚlenÚshapeÚcvtColorÚCOLOR_BGR2GRAYÚmaxÚresizeÚINTER_CUBICÚ	thresholdÚTHRESH_BINARYÚTHRESH_OTSUÚimage_to_stringÚjoinÚsortedÚmapr   Úescaper   r   r   r   Ú	ExceptionÚ	__class__Ú__name__ÚwarningÚdebug)Úimg_or_pathr)   r<   ÚimgÚgrayÚscaler    ÚbinaryÚtextÚaltr   ÚprefÚseg1Úseg2r   Úexcs                   r   Úocr_coderb   V   s«  € ÷ ÒžxØðÛð
/Ü@JÈ;ÔY\Ô^bÐXc×@dÑ@dŒc�jŠjœ˜[Ó)¬3×+;Ñ+;Ô<ÐjuˆØ‰;ØÜ8;¸C¿I¹I»È!Ó8KŒs�|Š|˜C¤×!3Ñ!3Ô4ÐQTˆà�:‰:�a‰=˜4ÓÜ˜˜4¤3 t§z¡z°!¡}°aÓ#8Ñ8Ó9ˆEÜ—:’:˜d D¨UÌCÏOÉOÑ\ˆDÜ—M’M $¨¨3´×0AÑ0AÄCÇOÁOÑ0SÓT‰	ˆØ×*Ñ*¨6Ð:LÐ*ÐMˆà�h‰h”vœc¤"§)¡)¨XÓ6¼CÈÑNÓOˆÜ�IŠI˜˜S˜EÐ!LÐMÈtÔUW×UbÑUbÓcˆÞØ Ÿw™w q›z×/Ñ/Ó1°1·7±7¸1³:×3CÑ3CÓ3EÀqÇwÁwÈqÃz×GWÑGWÓGY˜ˆDà‹| £Ø˜˜q   a¨ vÐ.�Ü—‘Ð6¸Ô=Ø�Ü�IŠI˜˜S˜EÐ!<Ð=¸tÄRÇ]Á]ÓSˆÞØ—g‘g˜a“j×&Ñ&Ó(Ð)¨!¯'©'°!«*¨°a·g±g¸a³j°\ÐBˆDÜ�K‰KÐFÈÔMØˆKð ð øôI ó ØÐÜ�‰Ð\Ô]Ùðûô< ó /Ø�=‰=×!Ñ!Ð%=Ó=Ø#ÐÜ�N‰NÐ`×aÐað ô �L‰L˜¨#×.Ð.Øûð/ús=   •K šAK5 Á)GK5 É BK5 Ë!K2Ë1K2Ë5
MË?1MÌ5MÍMc                  ó   • Sq g)z@Para los tests: vuelve a permitir el OCR tras un fallo simulado.FN)r;   © r   r   Úreset_ocr_statere   „   s
   € ð Ñr   Úpathzset[str] | NoneÚuse_ocrc                 ó  • [        U R                  5      S:X  a%  [        R                  " U [        R                  5      OU n[
        R                  U5      u  pVU(       d  [
        R                  U5      u  pVU(       d  g[        R                  S5         [
        R                  U5      u  px  nU(       aG  U HA  n	U	(       d  M  U	R                  5       (       d  M#  U	R                  5       R                  5       s  $    [
        R                  U5      u  n  nU(       a  UR                  5       R                  5       $ [        R                  " SSS9n[
        R                  UR!                  U5      5      u  n  nU(       a  UR                  5       R                  5       $ [        R"                  " USS	5      n[        R$                  " US	S
[        R&                  [        R(                  -   5      u  pn[
        R                  U5      u  n  nU(       a  UR                  5       R                  5       $ [*        R,                  " / SQ/ SQ/ SQ/[*        R.                  S9n[
        R                  [        R0                  " USU5      5      u  n  nU(       a  UR                  5       R                  5       $ U(       a  U(       a  [3        Ub  X5      $ U U5      $ g! [        R                   a!  n
[        R                  SU
5         Sn
A
GNSn
A
ff = f)u[   Texto crudo del QR de una imagen BGR (o, como Ãºltimo recurso, el cÃ³digo impreso por OCR).r/   Nu.   PatrÃ³n QR visible â€” intentando descodificaru   detectAndDecodeMulti fallÃ³: %sg      @)é   ri   )Ú	clipLimitÚtileGridSize)é   rl   r   r3   )r   éÿÿÿÿr   )rm   rl   rm   )Údtyperm   )rC   rD   r@   rE   rF   Ú_qr_detectorÚdetectÚdetectMultir>   rV   ÚdetectAndDecodeMultir   r   ÚerrorÚdetectAndDecodeÚcreateCLAHEÚapplyÚGaussianBlurrJ   rK   rL   ÚnpÚarrayÚfloat32Úfilter2Drb   )rX   rf   r)   rg   rY   Úfoundr    ÚokÚdatasÚdra   ÚdataÚclaheÚblurredr[   Úkernels                   r   Ú	detect_qrr„   Š   s7  € ô 58¸¿	¹	³NÀaÓ4GŒ3�<Š<˜œS×/Ñ/Ô0ÈS€Dô ×"Ñ" 4Ó(�H€EÞÜ×+Ñ+¨DÓ1‰ˆÞØÜ
‡L�LÐAÔBð=Ü&×;Ñ;¸DÓA‰ˆ�1�aÞÛ�ß�1˜Ÿ™Ÿ›ØŸ7™7›9Ÿ?™?Ó,Ò,ñ ô ×-Ñ-¨dÓ3�J€Dˆ!ˆQÞØ�z‰z‹|×!Ñ!Ó#Ð#ô �OŠO c¸Ñ?€EÜ×-Ñ-¨e¯k©k¸$Ó.?Ó@�J€Dˆ!ˆQÞØ�z‰z‹|×!Ñ!Ó#Ð#ô ×Ò˜t V¨QÓ/€GÜ—’˜g q¨#¬s×/@Ñ/@Ä3Ç?Á?Ñ/RÓS�I€AÜ×-Ñ-¨fÓ5�J€Dˆ!ˆQÞØ�z‰z‹|×!Ñ!Ó#Ð#ô �XŠX’z¢;²
Ð;Ä2Ç:Á:ÑN€FÜ×-Ñ-¬c¯lªl¸4ÀÀVÓ.LÓM�J€Dˆ!ˆQÞØ�z‰z‹|×!Ñ!Ó#Ð#ö –8Ü Ñ 0˜ÓDÐD°c¸8ÓDÐDØøô= �9‰9ó =Ü�‰Ð6¸×<Ò<ûð=ús*   Â*K ÃK ÃK Ã9K ËLË#K?Ë?L)N)r   N)NNT)Ú__doc__Úloggingr   Úpathlibr   Útypingr   r@   Únumpyrx   Ú	getLoggerrT   r>   ÚQRCodeDetectorro   r;   Úcompiler   r#   r   r-   rb   re   Úboolr„   rd   r   r   Ú<module>rŽ      sÝ   ðñó4 Û 	Ý Ý ã 
Û à	×	Ò	˜8Ó	$€à×!Ò!Ó#€ØÐ ð �*Š*Ð1Ó
2€ð1�cð 1˜h s™mô 1ñ&Ð6ð ÀHÈTÁNð Ð^hõ ð.+ Jð +°8¸C±=ô +ô\ð OSØ"ñ2˜ $™ð 2Ð:Kð 2Øð2Ø'/°¡}ö2r   