
    #_j>                    n   d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZ ddlZddlZ ee      j%                         j&                  d   Z eej*                  j-                  d eed	z                    Zed
z  dz  Zed
z  dz  dz  dz  dz  Zed
z  dz  dz  dz  dz  Zedz  dz  dz  dz  ZdZdOdZdPdZ G d d      Z e!dk(  rddl"Z"ddl#Z#ddl$Z$ e"jJ                  d      Z&e&jO                  d eed z        !       e&jO                  d"d#!       e&jO                  d$ eed z  d%z        !       e&jO                  d&d'd()       e&jQ                         Z)d* Z*i Z+ e$jH                  e)jX                        Z-e-rb e#j\                   e/e-d   d+,            D ]B  Z0e0j-                  d-      xs d.jc                         Z2e2s) ee0j-                  d/            e+e2<   D  e        Z3 ee)jh                        Z5g d0Z6 e7d-d1d2d3d1d2d4d5d2d6d7d2d8d9d2d:d;d2d<d5d2d=d5d>       dZ8dZ9 e*e)jt                        D ]X  Z2dZ;e6D ]  Z<e5e2 e< z  Z=e=j}                         se=Z; n e;-e)j~                  ri nd/e+j-                  e2d.      iZ@e3j%                  e;e@      ZAe+j-                  e2d.      ZBeAd   eAd   nd?ZCd.ZDeAd@   dAk(  rmeAd6   dBk(  reeBrceAd   r^e9dz  Z9 eEeAd          eEeB      k(  re8dz  Z8dCZDn= eF eEeAd          eEeB      z
        dk(  rdDZDndE eF eEeAd          eEeB      z
         ZDeAj-                  dF      rdGndHZGeAj-                  dI      rdGndHZH e7e2d1d2eBd1d2eCd5d2eAd6   d7d2eAdJ   d9d2eGd;d2eHd5d2eAd@   d5dKeAdL    d2eD        [ e9r e7dMe8 dNe9        yyy)Qu  濃度の解決（条件分岐）モジュール。

LLM送信前に「スープ濃度」を確定させる:
  - CSV等に濃度が与えられている場合      -> そのまま採用 (source=provided)
  - 与えられていない場合                  -> 画像モデルで推定 (source=model)
      湯気除外 -> スープ抽出 -> 濃度分類 の3段。
      スープ表面が一度も見えない動画は誤った数値を出さず status=判定不可 とする。

精度最優先のため、信頼度ゲートを設ける:
  - high   : 抽出信頼度>=conf_keep のフレームが multi_min 枚以上 かつ 投票占有率>=vote_min
  - medium : 抽出信頼度>=conf_keep のフレームが 1 枚以上
  - 判定不可: 上記に満たない（スープ表面が確認できない）

DensityResolver はモデルを一度だけ読み込む。resolve(video_path, metadata) を呼ぶ。
    )annotationsN)defaultdict)Path)Any   RAMEN_SUITE_DIRupstream_suitez03_soup-density-aisrcmodelscropsoup_valid_area_rfdetr_seg_v2zcheckpoint_best_ema.pthdensitydensity_classifier_extracted_v1zmodel.ptz04_steam-classificationsteam_detection_projectzsteam_classifier_best.pt)   スープ濃度brix_rawbrixr   valuec                    | yt        j                  dt        |       j                               }|r|j	                  d      S dS )uL   '12' '12度' などから数値文字列を取り出す。無ければ ''。 z\d+(?:\.\d+)?r   )researchstrstripgroup)r   ms     I/var/www/ramen.bs-engineer-server.com/local_batch_api/density_resolver.pynormalize_densityr   -   s=    }
		"CJ$4$4$67A1771:""    c                Z    t         D ]"  }t        | j                  |            }|s |c S  y)Nr   )DENSITY_COLUMNSr   get)metadatacolvs      r   density_in_metadatar&   5   s1     hll3/0H r   c                      e Zd Z	 	 	 	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZedd       ZddZddZddZedd       Z	edd       Z
edd	       Zedd
       ZdddZy)DensityResolverNc
                   || _         || _        || _        || _        || _        || _        |xs t        dz  dz  | _        | j                  j                  dd       || _	        d| _
        | j                  |	      | _        y )Nlocal_batch_apiresolver_cropsTparentsexist_okF)	conf_keepcrop_thresholdframes	multi_minvote_minhigh_mean_confROOT	crops_dirmkdir_device_name_loaded_load_calibration_calibration)
selfr/   r0   r1   r2   r3   r4   r6   device_namecalibration_paths
             r   __init__zDensityResolver.__init__>   s     #," ,"St.?'?BR'RTD9' 223CDr   c                8   dd l }| xs* t        t              j                         j                  dz  }|j                         sy 	 |j                  |j                  d            }|j                  dd      r|j                  d      r|S d S # t        $ r Y y w xY w)Nr   zcalibration.jsonzutf-8encodingenabledTmap)
jsonr   __file__resolveparentexistsloads	read_textr"   	Exception)pathrE   cal_pathdatas       r   r:   z!DensityResolver._load_calibrationW   s    QDN224;;>PP 	::h00'0BCD88It4%4RdR 		s   AB B 	BBc                    || j                   s||fS | j                   d   j                  t        |            }|||fS t        |      t        |      fS )ut   生の推定値に補正マップを適用。戻り値 (補正後, 生値)。マップが無ければそのまま。rD   )r;   r"   r   )r<   r   mappeds      r   
_calibratezDensityResolver._calibratec   sZ    = 1 1%<""5)--c%j9>%<6{CJ&&r   c                   | j                   ry dd l}|| _        |j                  | j                  dk7  r|j
                  j                         rdnd      | _        	 dd l}|j                  ddd      }|j%                  t&        | j                        }|j)                  d|v r|d   n|       |j+                  | j                        j-                          || _        dd l}ddlm} |j7                  |j9                  dd      |j;                  dd       |       g      | _        t>        j@                  jC                  dtE        tF                     ddl$m%}	m&}
 ddl'm(} |
| _)        || _*         |	dtE        tV              | jX                        | _-        d| _         y # t        $ rX ddl
m} dd lm} |j                  d 	      }|j                  |j                   d
   j"                  d      |j                   d
<   Y w xY w)Nr   cpucudaefficientnet_b0F   )
pretrainednum_classes)r   )weights)map_locationmodel_state_dict)
ToTensorV2   )heightwidth)g
ףp=
?gv/?gCl?)gZd;O?gy&1?g?)meanstd)build_predictorextract_one_image)predict_densitysmallT).r9   torch_torchdevicer8   rU   is_availabletimmcreate_modelrL   torchvisionr   torch.nnnnrV   Linear
classifierin_featuresload
STEAM_CKPTload_state_dicttoevalsteam_modelalbumentationsalbumentations.pytorchr^   ComposeResize	Normalizesteam_tfsysrM   insertr   CROP_SRCcrop.extract_soup_areard   re   density.predict_densityrf   _extract_one_image_predict_density	CROP_CKPTr0   crop_predictor)r<   rh   rl   steamr   rp   ckAr^   rd   re   rf   s               r   _ensure_modelszDensityResolver._ensure_modelsm   s   <<lld.?.?5.HUZZMdMdMf6mrs	R%%&7EWX%YE ZZ
Z=8Jb8Pb!34VXY""$ "5		HHCsH+KK28MKNL#
  	3x=)M;"3 /-gs9~tGZGZ[3  	R*!**4*8E#%99U-=-=b-A-M-Mq#QER 		Rs   F AH ?H c                   t        j                  |t         j                        }| j                  |      d   j	                  d      j                  | j                        }| j                  j                         5  t        | j                  j                  | j                  |      d      d   j                               cd d d        S # 1 sw Y   y xY w)N)imager   r   r   )dim)r   r   )cv2cvtColorCOLOR_BGR2RGBr   	unsqueezerw   rj   ri   no_gradfloatsoftmaxry   item)r<   	image_bgrrgbts       r   _steam_probzDensityResolver._steam_prob   s    ll9c&7&78MMM$W-77:==dkkJ[[  " 	W,,T-=-=a-@a,HNSSUV	W 	W 	Ws   <ACCc                    t        j                  dddddddt        |       gddd	
      }	 t        |j                  j                               S # t        $ r Y yw xY w)Nffprobez-verrorz-show_entrieszformat=durationz-ofzdefault=nw=1:nk=1T   )capture_outputtexttimeout        )
subprocessrunr   r   stdoutr   
ValueError)
video_pathrs     r   	_durationzDensityResolver._duration   sb    NNItWoGX!#6JI*.T2G	)** 		s   "A 	AAc                   t        t        j                  d            }t        j                  ddddd|dd	t        |       d
ddddt        |      gdd       |j                         rft        j                  t        j                  |j                         t        j                        t        j                        }|j                          |S y )N.jpg)suffixffmpegz-hide_bannerz	-loglevelr   z-ssz.3fz-iz	-frames:v1z-q:v2z-yT<   )r   r   )r   tempfilemktempr   r   r   rI   r   imdecodenp
frombuffer
read_bytesuint8IMREAD_COLORunlink)r   seekr   imgs       r   _framezDensityResolver._frame   s    /0.+wRVWZQ[c*o{CdTWXYTZ\&*B	8 88:,,r}}Q\\^RXXFHXHXYCHHJJr   c                    | j                   j                  dd       t        j                  d|t	        t        j
                        dg      \  }}|r | j                  |j                                y y )NTr,   r   \   )rH   r7   r   imencodeintIMWRITE_JPEG_QUALITYwrite_bytestobytes)rM   r   okes       r   _writezDensityResolver._write   s[    $6VS3s/G/G+H"*MNAQYY[) r   c                    | j                   d d \  }}t        ||z        t        ||z        }}||z
  dz  t        |dz        }}| |t        |||z         |||z   f   S )NrW   g333333?)shaper   min)r   ratiohwcwchx1y1s           r   _center_cropzDensityResolver._center_crop   sn    yy!}1QYQYBb&QAHB2c!R"Wo%r"r'z122r   c                   |xs i }t        |      }t        |      }|j                         s|xs d|rdnd|rdndddddd	d
d	S | j                          | j	                  |      }t        t        j                  dd| j                              }|j                  }g }d}d}	g }
t        |      D ]/  \  }}| j                  |t        d||z              }|)| j                  | d|ddz  }| j                  ||       | j                  |      }|
j!                  |       |	||	d
   k  r||f}	| j#                  || j                  t%        t&              d| j(                  | j(                  | j*                  dddd      }t-        |d         d
kD  st/        |d         }|||d   kD  rt%        |      |f}|| j0                  k\  s|j!                  |d   |f       2 |r|d
   n|	rt%        |	d         nd}|
rt/        t        j2                  |
            nd}|
rt/        t        j4                  |
            nd}d}d}t7        |      }d}|r#t9        t.              }|D ]L  \  }}| j;                  |t%        t<                    }|t-        |d         xx   |t/        |d         z  z  cc<   N t        |j?                         d       }t%        |d
         }|d   tA        |jC                               z  }tE        |      }t/        t        j2                  |D cg c]  \  }}|	 c}}            } || jF                  k\  r || jH                  k\  r| | jJ                  k\  rd nd!}d"| d#|d$d%| d$}|xr |d k(  }!tM        |d&      tM        |d&      d'}"| jO                  |      \  }#}$|#|$k7  rd(|$ini }%|r|ddd)||!||#|d*| tE        |      d+|"|%S |#|#d|d)||!||tE        |      d	|"|%S dddddd||d
d	|"S c c}}w ),u  濃度の有無に関わらず、まず『スープ可読性レイヤー』を常に通す。

        可読性（スープ表面が読めるか = RF-DETR抽出信頼度）を判定し、LLMに見せる最良フレームを選ぶ。
        その上で濃度を確定する: CSV提供値があればそれ、無ければ画像モデル推定。
        可読性は濃度の出所と独立に算出される（湯気で表面が見えなければ assessable=False）。
        Nprovidedmodelgivennoneu   判定不可Fr   u   動画が見つからないr   )	r   source
confidencestatusreadable
assessablebest_frame_pathnoteframes_usedg?gffffff?r   _src_f02dr   rg   r   T)
image_path
output_dircheckpoint_path
model_size	thresholdlow_confidence_threshold	predictor
save_alphapreserve_filenameoutput_kindskip_fallback_outputextract_statusextract_confidencer   soup_crop_pathg      ?u4   スープ表面を確認できず（湯気/構図）predicted_labelr   c                    | d   S )Nr    )kvs    r   <lambda>z)DensityResolver.resolve.<locals>.<lambda>   s
    RU r   )keyhighmediumu   スープ可視u   枚/占有率z.2fu   /抽出信頼度平均   )
steam_mean	steam_minmodel_estimate_rawr   u   CSV提供値 / 可読性: )r   r   r   r   r   r   r   model_estimatemodel_estimate_confidencer   r   )(r&   r   rI   r   r   listr   linspacer1   stem	enumerater   maxr6   r   r   appendr   r   r   r0   r   r   r   r/   rb   r   boolr   r   DENSITY_CKPTitemssumvalueslenr2   r3   r4   roundrR   )&r<   r   r#   r   durratiosr   
candidatesbest_srcleast_steamsteam_probsir   r   fpspresecr   r   r   model_value
model_confr   readability_notescore	crop_pathdbestsharen_	mean_confr   steam_fields	cal_value	raw_value
cal_fieldss&                                         r   rG   zDensityResolver.resolve   s    >r&x0*%
  "%-xU\-5'6^ %Ur9!M M
 	nnZ(bkk$dkk:;
f% 	CDAq++j#c37*;<C{TF&3t"<<BKKC !!#&Br""b;q>&9!2h))$..#i."d.A.A)-)<)<H[H[ Evdh	 * jC
 3'()A-3345#rHQK'7 #B}H'%%s+;'<b&AB)	C. *2(1+{KN8K`b4?U277;/0S
2=E"&&-.3	 

#Q&E!+ P	2)))S5FGc!-./0Bq9O4OO0P u{{}*:;Dd1g,KGc%,,.11EJAbggz&Bear&BCDI$%$7ET]]<RW`dhdwdwWw  GJ!0=sKabkloapq6*"6
&+J&:yZ[I\]  ${;	9;D	;Q*I6XZ
 %7"
'6&/j89I8JK\_`j\k	2
 #2
 '12 2  &'"
'6,S_l Q]l akl l
 R`!?(L>JL 	L9 'Cs    P )	g      ?g?   r   gffffff?g
ףp=
?NautoN)r/   r   r0   r   r1   r   r2   r   r3   r   r4   r   r6   Path | Noner=   r   r>   r'  returnNone)rM   r'  r(  dict[str, Any] | None)r   z
str | Noner(  ztuple[str | None, str | None])r(  r)  )r(  r   )r   r   r(  r   )r   r   r   r   )rM   r   r(  r)  )g333333?)N)r   z
str | Pathr#   r*  r(  dict[str, Any])__name__
__module____qualname__r?   staticmethodr:   rR   r   r   r   r   r   r   rG   r   r   r   r(   r(   =   s      $ $!%!(,EE E 	E
 E E E E E &E 
E2 	 	'%PW   	 	 * * 3 3aLr   r(   __main__uK   濃度解決の動作確認（CSV濃度を伏せて画像推定を検証）)descriptionz--video-diru   学習データ)defaultz--idsz191-200z--label-csvu   *グリッド 10.csvz--simulate-missing
store_trueu9   CSV濃度を伏せてモデル推定経路を強制する)actionhelpc                \   | j                         } d| v rQd| vrM| j                  dd      \  }}t        t        |      t        |      dz         D cg c]  }t	        |       c}S | j                  d      D cg c]#  }|j                         s|j                         % c}S c c}w c c}w )N-,r   )r   splitranger   r   )slohir  xs        r   	parse_idsr?  /  s    GGI!81WWS!_FB$)#b'3r7Q;$?@qCF@@#$773<=a1779	== A=s   B$9B)B)z	utf-8-sigrA   IDr   r   )z.movz.mp4z.MOVz.MP4z>4 u   人間u	   解決値z>6r   z>9u	   信頼度z>7u   可読z>5u	   評価可u   状態u     備考r7  r   r   r   u   ✓u   ±1u   差r   u   ○   ×r   r   z  r   u%   
モデル推定経路: 完全一致 /)r   r   r(  r   )r#   r+  r(  r   )I__doc__
__future__r   osr   r   r   r   collectionsr   pathlibr   typingr   r   numpyr   rF   rG   r-   r5   environr"   r   SUITEr   r   r  ru   r!   r   r&   r(   r,  argparsecsvglobArgumentParserapadd_argument
parse_argsargsr?  human	label_csvfs
DictReaderopenr   r   vidresolver	video_dirvdirextsprintcorrectr  idsvpathr   crI   simulate_missingmetar  r   valmarkr   absrdasbr   r   r   <module>rk     s    # 	 	  
  #   
 H~''* 	RZZ^^-s4:J3J/KLM''%/((83f<?^^azz	++h6BEffiss..1JJXUXrr
D#dL dLN z	 	 	 -z	{BOOM3t6G/G+HOIOOGYO/OOM3t6G/GJ`/`+aObOO(DO  A==?D> E	4>>	"B	RU[ AB 	IA55;$"++-C.quu5F/GHc
	I
  HD+D	T"IQxm1[$4Ahr]!KPRCSSTU]^`Taabcnoqbrrst|}  tA  AI  J  KG	A" O 	A#qc{"Axxz		
 =**r1BEIIcSUDV0Wud+IIc2!'l6c'lCx=D S]g%=!GFA3w< CF*1UdSW&Q/0A5SS\!23q6!9:;<ggj)Utww|,e$R!BqR#h-);1S=Nr<RRSTVWYSZZ[\_`b[ccdehiqersudvvxy|  ~D  zE  yF  FG  HL  GM  N  	O1O2 	6wiqDE 	u r   