ビデオからフレームを段階的に読み取ることは可能ですか(たとえば、ビデオストリームの5フレームごとに読み取りたい)。現在、これを回避策として実行していますが、あまり効率的ではありません。
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.read(NextFrameBGR);
必要なフレームを取得するために5つのフレームをループする必要がないように、何か提案はありますか?
できることは多くなく、OpenCVの欠点だけではありません。ご存知のように、現代のビデオコーデックは、一般に複雑な獣です。より高い圧縮率を得るために、フレームのエンコーディングは、多くの場合、以前のフレームに依存し、時には連続するフレームにも依存します。
そのため、ほとんどの場合、必要がない場合でも、目的のフレームの前にフレームをデコードする必要があります。
ビデオファイルを具体的にエンコードするには、かなり重要なトリックがあります。そのため、N番目のフレームごとに取得する方が安価ですが、一般的なケースでは実現できません。
つまり、OpenCVが提供するシーク機能を試すことができます( OpenCV Seek Function/Rewind を参照)。状況によっては、より高速に動作する場合と動作しない場合があります。しかし、個人的には、私はそれに賭けません。
次のように、私はPython 3で単純なカウンターを使用してキャプチャをそのカウンターのフレームに設定することで成功しました:
import cv2
cap = cv2.VideoCapture('XYZ.avi')
count = 0
while cap.isOpened():
ret, frame = cap.read()
if ret:
cv2.imwrite('frame{:d}.jpg'.format(count), frame)
count += 30 # i.e. at 30 fps, this advances one second
cap.set(1, count)
else:
cap.release()
break
私はwith
ステートメントを使用してこれをもう少しPythonicにする方法を見つけようとしましたが、CV2ライブラリがそのために更新されたとは思いません。
これが私が提案するものです:
CvCapture* capture = cvCaptureFromFile("input_video_path");
int loop = 0;
IplImage* frame = NULL;
Mat matframe;
char fname[20];
do {
frame = cvQueryFrame(capture);
matframe = cv::cvarrToMat(frame);
cvNamedWindow("video_frame", CV_WINDOW_AUTOSIZE);
cvShowImage("video_frame", frame);
sprintf(fname, "frame%d.jpg", loop);
cv::imwrite(fname, matframe);//save each frame locally
loop++;
cvWaitKey(100);
} while( frame != NULL );
すべてのフレームをローカルに保存したので、必要なn番目のフレームをすばやく読み取ることができます。
[〜#〜] catuion [〜#〜]:私が持っていた12秒のサンプル動画は、200以上の画像で構成されていました。これは多くのスペースを消費します。
シンプルでありながら効果的な最適化は、使用しているアプローチまたは@sergieによって提案されたアプローチを使用してn番目のフレームを読み取ることです。この後、インデックスを付けて画像を保存できるため、後で同じインデックスでクエリを実行すると、現在のようにフレームをスキップする必要がなく、保存された画像が返されます。このようにして、照会していないフレームの保存に無駄になっていたスペースと、不要なフレームの読み取りと保存にかかる時間を節約できます。
OpenCVで同じ目標を達成したとき、フレームレートやフレームの総数に関係なく、ビデオの1秒あたりに必要な「キーフレーム」の数を調整しました。したがって、これにより、ターゲットのKPSを指定すると、N番目のキーが取得されます。
# python3.6 code using OpenCV 3.4.2
import cv2
KPS = 5 # Target Keyframes Per Second
VIDEO_PATH = "path/to/video/folder" # Change this
IMAGE_PATH = "path/to/image/folder" # ...and this
EXTENSION = ".png"
cap = cv2.VideoCapture(VIDEO_PATH)
# Set frames-per-second for capture
fps = round(cap.get(cv2.CAP_PROP_FPS))
hop = round(fps / KPS)
curr_frame = 0
while(True):
ret, frame = cap.read()
if not ret: break
if curr_frame % hop == 0:
print('Creating... {0}'.format(name,))
name = IMAGE_PATH + "_" + str(curr_frame) + EXTENSION
cv2.imwrite(name, frame)
curr_frame += 1
cap.release()
すべてのフレームをロールスルーしているが、Nとしてhop
を使用してN番目のフレームのみを書き込むことに注意してください。
次のフレームに移動するには、grab
関数を使用する必要があります。そして、必要なフレームのデコードにはretrieve
のみを使用してください。
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.grab();
bSuccess = cap.retrieve(NextFrameBGR);
同じ問題が発生しました。私がしたことはこれだけでした:
import cv2
vs = cv2.VideoCapture("<path of your video>.mp4")
print("Showing frames...")
c=1
while True:
grabbed, frame = vs.read()
if c%5==0:
cv2.imshow('Frame',frame)
cv2.waitKey(1)
c+=1
vs.release()
お役に立てれば。
私は this repoを使用します!
主なアイデアは:
main.py
from camera import VideoCam
SKIPFRAME = 8
url = 0
v1 = VideoCam(url)
v1.check_camera(v1.cap)
ct = 0
while True:
ct += 1
try:
ret = v1.cap.grab()
if ct % SKIPFRAME == 0: # skip some frames
ret, frame = v1.get_frame()
if not ret:
v1.restart_capture(v1.cap)
v1.check_camera(v1.cap)
continue
# frame HERE
v1.show_frame(frame, 'frame')
except KeyboardInterrupt:
v1.close_cam()
exit(0)
camera.py
import cv2
import logging
class VideoCam():
def __init__(self, url=0):
self.url = url
self.cap = cv2.VideoCapture(self.url)
self.get_frame()
self.get_frame_read()
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
def check_camera(self, cap):
logging.info('Camera {} status: {}'.format(self.url, cap.isOpened()))
def show_frame(self, frame, name_fr='NAME'):
cv2.imshow(name_fr, frame)
# cv2.imshow(name_fr, cv2.resize(frame, (0, 0), fx=0.4, fy=0.4))
cv2.waitKey(1)
def get_frame(self):
return self.cap.retrieve()
def get_frame_read(self):
return self.cap.read()
def close_cam(self):
self.cap.release()
cv2.destroyAllWindows()
def restart_capture(self, cap):
cap.release()
self.cap = cv2.VideoCapture(self.url)
ishan Shahのコードに基づいて、誰かが5番目ごとのフレームをキャプチャしてjpgとして保存する必要がある場合:
import cv2
vid = cv2.VideoCapture('C:/python_works/creatives_gardenscapes/0c52b83ed1dec617092aaf83278f12ad.mp4')
if not os.path.exists('images'):
os.makedirs('images')
index = 0
while(True):
ret, frame = vid.read()
if not ret:
break
name = 'C:/python_works/creatives_gardenscapes/frames/0c52b83ed1dec617092aaf83278f12ad' + str(index) + '.jpg'
if index%50==0:
cv2.imwrite(name, frame)
index += 1
エンコーディングスキームは一般に非常に複雑であるため、ランダムフレームを抽出することはできません。たとえば、MPEG-4では、2つのフレーム間の差異を含む情報のみが保存されるため、明らかに前のフレームが必要です。