본문 바로가기
코딩과 알고리즘

파도(19) - 파이스크립트 동영상 재생! 근데 넘 느려요 ㅎ..

※ '파도'는 파이스크립트 도전기의 줄임말입니다.

파이스크립트에서 동영상 재생

지난 게시글에서 연재되는 글입니다.

https://itadventure.tistory.com/560

 

파도(18) - 랜덤고양이 이미지 불러오기

※ '파도'는 크레이의 '파이스크립트 도전기'의 줄임말입니다. 지난 게시글에서 연재되는 글입니다. : https://itadventure.tistory.com/559 파도(17) - 자바스크립트와 파이스크립트, 손잡다! 🍬 '파도'는

itadventure.tistory.com

 

2fps! 뭐라고?!

 

2fps!
크레이가 알아낸 파이스크립트의 현재 동영상 재생 속도 한계입니다 ㅎ..

fps 는 frame per sec 이라고 1초에 몇장면을 보여줄 수 있는지 의미하는 척도인데요.
순수 파이썬은 동영상 재생 속도가 매우 빠르지만
파이스크립트 개발버전에서는 1초에 2개의 장면이 한계입니다. ( 2022. 9. 4 기준 )

그러니 영상이 뚝뚝 끊겨서 보이지요.
뭐 나중에는 좋은 방법이 제공될거라 생각됩니다.
공식적으로 공개된 내용은 없습니다.
크레이도 이것 저것 실험하면서 간신히 알아낸거라서요 ㅎ..
( 뭐 그러다 실력이 느는 거지요 =_=.. )

우선 미리 결과를 보실 분은 아래 URL을 방문해 주세요.
http://dreamplan7.cafe24.com/pyscript/pycam-1.html

그러면 크레이가 유니티로 만든 샘플 프로젝트 영상이 뚝뚝 끊겨서 보이실 겁니다.
원본이 그런건 아닙니다. 파이스크립트에서 늦게 재생이 되는거니까요.

 

스크립트 공개!

 

자, 그러면 오늘도 시원시원(?)하게 전체 스크립트를 공개하고 시작하겠습니다!

<!DOCTYPE html>
<html> 
  <head> 
    <link 
      rel="stylesheet" 
      href="https://pyscript.net/alpha/pyscript.css" 
    /> 
    <script 
      defer 
      src="https://pyscript.net/alpha/pyscript.js"
    ></script> 
    <py-env>
    - matplotlib
    - opencv-python
    - paths:
      - http://dreamplan7.cafe24.com/pyscript/Unity3DCam.mp4
    </py-env>
    <py-config>
      - autoclose_loader: true
      - runtimes:
        -
          src: "https://cdn.jsdelivr.net/pyodide/dev/full/pyodide.js"
          name: pyodide-0.20
          lang: python
    </py-config>
  </head>
  <body> 
    <link rel="stylesheet" href="pytable.css"/>
    <div id="frameNo"></div>
    <div id="graph"></div>
<py-script>
import matplotlib.pyplot as plt
import cv2
from js import document, setInterval
from pyodide.ffi import create_proxy
import gc

캡쳐 = cv2.VideoCapture('Unity3DCam.mp4')
프레임번호 = 120

def ShowFrame():
  global 캡쳐, 그래프, cv2, plt, 프레임번호, document
  캡쳐.set(cv2.CAP_PROP_POS_FRAMES, 프레임번호)
  ret, frame = 캡쳐.read() # 두 개의 값을 반환하므로 두 변수 지정
  그래프 = plt.figure(figsize=(8, 6))
  plt.axis('off')
  ax = plt.gca()
  ax.margins(x=0, y=0)
  그래프.tight_layout()
  plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
  pyscript.write('graph', 그래프)
  plt.close(그래프)
  document.getElementById('frameNo').innerHTML=프레임번호
  프레임번호=프레임번호+15
  gc.collect()

setInterval(create_proxy(ShowFrame), 500)
</py-script> 
  </body> 
</html>


지난 모든 글들을 읽어보신 분이라면 새로운 것들이 잔뜩 추가된 것을 보실 수 있을텐데요.
사실 이걸 만드려고 일주일간 헤메였습니다. ( 잉여 시간만요 ㅎ.. )

 

opencv 모듈 불러오기

 

새로운 부분들을 하나씩 풀어보도록 하겠습니다 :)
우선 <py-env> 태그에 2가지 새로운 것이 보이는데요. ( 빨간색 )


<py-env>
- matplotlib
- opencv-python
- paths:
  - http://dreamplan7.cafe24.com/pyscript/Unity3DCam.mp4
</py-env>


matplotlib 모듈은 지난 게시글을 읽어오신 분은 아실테니 넘어가고,
opencv-python 이라는 모듈이 있는데요.
동영상/이미지 파일을 읽어와 가공할 수 있는 기능입니다. mp4 같은 파일도 말이지요.

그리고 http://dreamplan7.cafe24.com/pyscript/Unity3DCam.mp4 파일은 크레이가 준비한 영상 파일입니다. 크레이의 홈페이지에 올려놓고 이렇게 연결해 놓으면 파이스크립트 내부에서 사용하실 수 있습니다.

 

파이스크립트 개발 버전 사용하기

 

그 다음으로 새로운 태그가 보이는데요.


<py-config>
  - autoclose_loader: true
  - runtimes:
    -
      src: "https://cdn.jsdelivr.net/pyodide/dev/full/pyodide.js"
      name: pyodide-0.20
      lang: python
</py-config>


위 태그는 현재 개발중인 버전을 가져와 적용하는 기능이라고 합니다.
사실 opencv 모듈은 아직 알파버전에서는 사용할 수 없습니다.
하지만 개발 버전에는 포함되어 있다는 군요.
개발 버전의 파이스크립트를 사용하려면 이 태그를 쓰면 됩니다.
차후에 정식 버전에서 빠른 속도로 사용할 수 있기를 기대합니다 :)

이어서 여러 모듈을 불러오게 되어 있는데요.


import matplotlib.pyplot as plt
import cv2
from js import document, setInterval
from pyodide.ffi import create_proxy
import gc


오늘 예제에서 모두 사용되는 모듈입니다.

 

동영상 불러와 제어하기

 

제일 처음에 사용되는 cv2 는 동영상/이미지를 처리하는 모듈인데요.
동영상을 불러오려면 아래와 같이 사용하면 됩니다.


 

캡쳐 = cv2.VideoCapture('Unity3DCam.mp4')


그러면 캡쳐 변수는 동영상을 제어하기 위한 오브젝트, 기능 덩어리가 됩니다.
매우 다양한 기능이 있는데요. 본 소스에서는 아래와 같이 2가지를 사용하였습니다.


캡쳐.read()
영상에서 1개의 프레임을 읽습니다.

캡쳐.set(cv2.CAP_PROP_POS_FRAMES, 프레임번호)
영상의 특정 프레임으로 이동합니다.


 

타이머 이벤트 연결!

 

그리고 이어서 지난번 릿지 회귀(Ridge Regression)와 라쏘 회귀(Lasso Regression)에서 잠깐 다뤘던
프록시 개념이 다시 등장하는데요.
지난번에는 버튼의 클릭 이벤트를 연결하였지만, 이번에는 타이머 이벤트를 연결하였습니다.

아래 코드는 자바스크립트의 타이머를 이용해서 ShowFrame 이라는 파이썬 함수를
0.5초에 한번씩 호출해 주는 데요. 이 때 프록시를 사용합니다.


setInterval(create_proxy(ShowFrame), 500, 0)


파이스크립트 개발 버전에서는 프록시 기능을 사용하기 위한 모듈 선언도 약간 달라졌는데요.
원래는 아래와 같이 선언하도록 되어 있었습니다만,


from pyodide import create_proxy


아래와 같이 바뀌었습니다.
위와 같이 사용해도 작동은 하는데요. 곧 위 사용법은 없어질 거라는 경고 메시지가 뜹니다.
뭐 실제로 없어질지는 추후 정식 버전 출시에 따라 달라질 수 있지만요 :)


from pyodide.ffi import create_proxy



이제 0.5초마다 ShowFrame() 함수가 불려질텐데요. 더 짧은 주기로 호출하는건 문제가 안되지만,
영상에서 프레임을 불러와 이미지로 화면에 찍어주는 부분에서 0.3~0.5초 가량의 지연이 생깁니다.
(뭐.. 크레이의 컴퓨터가 느린걸 수도 있지만요 ㅎㅎ)

그러니 더 빨리 불러봐야 PC의 부담만 가중됩니다.

 

전역변수 연결하기

 

ShowFrame() 함수는 동영상의 장면을 읽어와 화면에 표시해주는 역활을 하는데요.

처음에 표시된 global 어쩌고 부분은 전역변수 사용이라는 기능입니다.


global 캡쳐, 그래프, cv2, plt, 프레임번호, document


위와 같이 선언하면 캡쳐, 그래프, cv2, .. 등 함수 밖에서 선언한 변수들을 사용할 수 있는데요.
매번 함수에 파라미터로 넘겨주는게 번거롭기 때문에 위와 같이 간단한 예제에서 사용하거나 또는 큰 프로젝트에서 효율성을 위해 사용하는 방법이기도 합니다.

 

동영상 프레임 장면 받아오기

 

이어서 동영상의 특정 장면으로 이동하여 하나의 장면을 받아오는 기능이 실행됩니다.
프레임 번호는 처음에 120이란 값이 들어 있기 때문에, 먼저 120번째 프레임으로 이동하지요.


캡쳐.set(cv2.CAP_PROP_POS_FRAMES, 프레임번호)
ret, frame = 캡쳐.read()


ret 에는 장면을 받아오는데 성공했는지 실패했는지의 값이,
frame 에는 해당 장면이 넘파이 배열 형태로 저장되는데요.
압축을 완전히 풀어버린 형태라서 크기가 매우 큽니다.

[[[ 25 34 34] [ 25 34 34] [ 25 34 34] ... ]

 

이미지 표시!


이제 이미지를 표시하기 위해 pyplot 을 초기화합니다.


그래프 = plt.figure(figsize=(8, 6)) # 그래프 크기
# 그래프의 축이나 여백을 없앱니다.
plt.axis('off')
ax = plt.gca()
ax.margins(x=0, y=0)
그래프.tight_layout()


Q
"pyplot? cv2.imshow 를 사용하면 되지 않나요?"

A
크레이도 cv2.imshow 가 매우 빠른 것은 알아 보았지만 파이스크립트 현재 버전에서는 아직 기능이 지원되지 않는 것으로 보입니다. 사실 이 것 때문에 일주일간 헤메였지요.


그리고 실제 이미지를 표시하는 명령은 아래와 같습니다.


plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
pyscript.write('graph', 그래프)


간단하게 아래와 같이 사용해도 되지만, 색상이 이상하게 나옵니다.
그것은 영상에서는 색상 배열 순서가 파랑/녹색/빨강 순이지만,
이미지에서의 색상 배열 순서는 빨강/녹색/파랑 순이기 때문에
색상의 배열 순서를 뒤집어 주어야 하기 때문이지요.
영상을 이미지로 표시할 때는 위 방법을 사용해주셔야 합니다.


plt.imshow(frame)


 

그래프 객체 삭제

 

그리고 아래 명령이 등장하는데요.
금방 만들었던 그래프 오브젝트를 삭제합니다. 이 부분을 처리하지 않으면 점점 재생 속도가 느려지더라구요.
매번 함수가 호출될 때마다 그래프를 생성했다가 이미지를 표시하고 삭제하는 과정을 반복하니 느릴 수 밖에 없지요.


plt.close(그래프)


 

동영상의 프레임 이동을 위한 변수값 변경

 

화면에 현재 프레임 위치를 표시하고 다음 프레임을 재생하기 위해 프레임번호 변수의 값에 15를 더해줍니다.
그러면 다음에 프레임을 읽을 때 15프레임 뒤의 장면을 읽겠지요.


document.getElementById('frameNo').innerHTML=프레임번호

프레임번호=프레임번호+15



가베지 콜렉터, 쓰레기 수거

 

그 다음 보이는 gc.collect() 명령은 가베이지 콜렉터라고 해서,
파이썬 엔진을 사용할 때 점점 누적되는 메모리를 정리해 줍니다.
보통 쓰레기 수거라고도 하는데요. 파이썬에서는 간단하게 명령어 1줄로 사용할 수 있습니다.
일정 주기로 호출해주는 것이 좋지만 여기서는 그냥 간편하게 매번 정리해주도록 하였습니다. 


gc.collect()


 

마무~리

 

오늘은 파이스크립트에서 opencv 모듈을 이용하여
동영상 파일을 불러와 재생하는 부분을 다뤄보았습니다.

파이썬에 대해 어느 정도 알고 계신 분이라면 수월하게 보실 수 있겠지만,
학습을 위해 오신 분들이라면 새로운 개념들이 많아져 본문을 여러번 읽어야 이해가 되실 수 있습니다.
프로그래밍이란 원래 그런 것이니까요 :)

친목으로 오시는 분들에게는, 그저 감사합니다 :)


아무쪼록 필요하신 분에게는 유용한 정보가 되셨기를 바라면서
오늘도 방문하시고 글 읽어주신 여러분께 감사드립니다.

유용한 정보라면 공감 한방, 댓글은 베리 베리 굿잡!
감사합니다!


다음 게시글 : https://itadventure.tistory.com/562

 

파도(20) - 동영상 얼굴인식

※ '파도'는 크레이의 파이스크립트 도전기의 줄임말입니다. 지난 게시글에서 연재되는 글입니다 => https://itadventure.tistory.com/561 파도(19) - 파이스크립트 동영상 재생! 근데 넘 느려요 ㅎ.. ※ '파

itadventure.tistory.com