
    i                        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Zddl	m
Z
 ddlmZ ddZ	 	 	 	 	 	 ddZddZdd	Zdd
Zdd	 	 	 	 	 	 	 ddZy)u  OCR 기반 키프레임 검증 모듈.

IDS §0.1 — 첫/중간/끝 프레임 한글 텍스트 존재 확인
- pytesseract 사용 가능 시: OCR로 텍스트 추출
- pytesseract 미설치 시: PIL 픽셀 분석으로 콘텐츠 존재 여부 확인 (fallback)
    )annotationsN)Path)Optionalc                    t         j                  j                  dd      } | r$t        |       j	                         rt        |       S d}t        |      j	                         r|S t        j                  d      }|r|S t        d      )u/   ffmpeg 실행 파일 경로를 반환합니다.
FFMPEG_BIN z/home/jay/.local/bin/ffmpegffmpegu"   ffmpeg를 찾을 수 없습니다.)	osenvirongetr   existsstrshutilwhichFileNotFoundError)env_bin	local_binpath_bins      4/home/jay/workspace/skills/motion-cardnews-ko/ocr.py_get_ffmpeg_binr      so    jjnn\2.G4='')7|-II||H%H
@
AA    c                l   t        |       } t        |      }|j                  dd       t               }t        j                  |dt        |       dddgdd      }t        |j                        }t        dd	      t        d	|d
z        t        d	|d	z
        d}g }dD ]  }||   }|d| dz  }	|dd|ddt        |       ddddt        |	      g}
t        j                  |
dd      }|j                  dk7  rt        d| d| d|j                         |j                  |	        |d   |d   |d   fS )uB  동영상에서 첫/중간/끝 프레임을 PNG로 추출합니다.

    Args:
        video_path: 원본 MP4 파일 경로
        output_dir: 추출된 PNG를 저장할 디렉토리

    Returns:
        (first_frame, middle_frame, last_frame) Path 튜플

    Raises:
        RuntimeError: ffmpeg 실행 실패 시
    Tparentsexist_okz-iz-fnull-)capture_outputtextg        g?g       @firstmiddlelast	keyframe_z.pngz-yz-ssz.3fz	-frames:v1z-q:v2r   u   키프레임 추출 실패 (z, t=zs):
      )r   mkdirr   
subprocessrunr   _parse_durationstderrmax
returncodeRuntimeErrorappend)
video_path
output_dir
ffmpeg_binprobeduration
timestampsframe_pathslabeltout_pathcmdresults               r   extract_keyframesr>   $   sq     j!Jj!JTD1 "J NN	T3z?D&#>E
 u||,H S#c8c>*CC(J !K, %u)E7$ 77aW#j/CM
 DtD!.ugT!E&--Q  	8$!%$ q>;q>;q>99r   c                    t        j                  d|       }|sy|j                         \  }}}t        |      dz  t        |      dz  z   t	        |      z   S )uK   ffmpeg stderr에서 Duration 값을 파싱합니다. 실패 시 1.0 반환.z#Duration:\s*(\d+):(\d+):(\d+\.?\d*)g      ?i  <   )researchgroupsintfloat)ffmpeg_stderrmhmnss        r   r,   r,   _   sN    
		8-HAxxzHAr1q6D=3r7R<'%(22r   c                r   	 ddl }ddlm} |j                  t	        |             }|j                  |dd      j                         S # t        $ r t        j                  dt        d	       Y y
t        $ r?}t        j                  dt        |      j                   d| t        d	       Y d}~y
d}~ww xY w)u+  프레임 이미지에서 텍스트를 추출합니다.

    pytesseract가 없으면 빈 문자열을 반환하고 경고를 발생시킵니다.

    Args:
        frame_path: PNG 프레임 파일 경로

    Returns:
        추출된 텍스트 문자열 (pytesseract 없으면 빈 문자열)
    r   NImagezkor+engz--psm 6)langconfigu   pytesseract가 설치되지 않았습니다. OCR을 건너뜁니다. 설치: pip install pytesseract && apt-get install tesseract-ocr tesseract-ocr-korr(   
stacklevelr   u   OCR 오류 (z): )pytesseractPILrM   openr   image_to_stringstripImportErrorwarningswarnUserWarning	Exceptiontype__name__)
frame_pathrR   rM   imges        r   	ocr_framera   h   s    jjZ)**3Yy*QWWYY a		
  T!W%5%5$6c!={WXYs   AA %B6/B675B11B6c                J   	 ddl m} |j                  t        |             j	                  d      }t        |j                               }|syt        |      t        |      z  t        fd|D              t        |      z  }|dz  }dkD  xr |dkD  S # t        $ r Y yw xY w)u   PIL로 프레임이 비어있지 않은지(콘텐츠 존재 여부)를 확인합니다.

    mean > 5 AND stddev > 5 이면 콘텐츠가 있다고 판단합니다.
    r   rL   LFc              3  .   K   | ]  }|z
  d z    yw)r(   N ).0pmeans     r   	<genexpr>z&_is_frame_non_blank.<locals>.<genexpr>   s     71DQ7s   g      ?   )
rS   rM   rT   r   convertlistgetdatasumlenr[   )r^   rM   r_   pixelsvariancestddevrh   s         @r   _is_frame_non_blankrs      s    
jjZ)11#6ckkm$6{S[(777#f+ESax&FQJ& s   A
B AB 	B"!B")r3   c               $   | t        t        j                  d            }nt        |      }|j                  dd       	 t	        | |      \  }}}i }	d|fd|fd|ffD ]J  \  }
}t        |      d	k(  }rt        fd|D              }nt        |      }|
t        |      |d|	|
<   L |	S # t
        $ rW}t        j                  d| t        d       d	d	d
dd}t        |d      t        |d      t        |d      dcY d}~S d}~ww xY w)uw  동영상의 첫/중간/끝 프레임에서 한글 텍스트를 검증합니다.

    IDS §0.1 준수: pytesseract 없으면 PIL 픽셀 분석으로 폴백.

    Args:
        video_path: 검증할 MP4 파일 경로
        expected_korean_chars: 존재 여부를 확인할 한글 문자/문자열 목록
        output_dir: 키프레임 저장 디렉토리 (None이면 임시 디렉토리 사용)

    Returns:
        3개 엔트리의 딕셔너리:
        {
            "first": {"frame": "first", "ocr_text": str, "has_expected": bool, "fallback": bool},
            "middle": {...},
            "last": {...},
        }
    Nmotion_ocr_)prefixTr   u   키프레임 추출 실패: r(   rP   r   F)frameocr_texthas_expectedfallbackr!   )rw   r"   r#   r    c              3  &   K   | ]  }|v  
 y w)Nre   )rf   chrx   s     r   ri   z)validate_korean_frames.<locals>.<genexpr>   s     N"rX~Ns   )r   tempfilemkdtempr)   r>   r[   rX   rY   rZ   dictra   anyrs   bool)r2   expected_korean_charsr3   tmp_dirr!   r"   r#   r`   emptyresultsr9   r^   is_fallbackry   rx   s                 @r   validate_korean_framesr      s=   . x''}=>z"dT2/
GDvt  "G&.60BVTNS 
zZ("nN8MNNL /z:L   .#	

" N1  4QC8+RST"eQUVe73tEQY?Zdhinv|d}~~	s   B/ /	D8AD
D
D)returnr   )r2   r   r3   r   r   ztuple[Path, Path, Path])rF   r   r   rE   )r^   r   r   r   )r^   r   r   r   )r2   r   r   z	list[str]r3   zOptional[Path]r   r   )__doc__
__future__r   r
   rA   r   r*   r}   rX   pathlibr   typingr   r   r>   r,   ra   rs   r   re   r   r   <module>r      s    # 	 	      B"8:8:8: 8:v3:. "&	77$7 	7
 
7r   