動画ファイルのフレーム数

先のエラーを追いかけます。

$ ls -lt out_v26/bw_near_2* | head
-rw-r--r--@ 1 kondoh  staff  416097  4 21 13:43 out_v26/bw_near_2.mp4
-rw-r--r--  1 kondoh  staff   28439  4 21 13:43 out_v26/bw_near_200080.jpg
-rw-r--r--  1 kondoh  staff   28495  4 21 13:40 out_v26/bw_near_200079.jpg
-rw-r--r--  1 kondoh  staff   28084  4 21 13:37 out_v26/bw_near_200078.jpg
-rw-r--r--  1 kondoh  staff   27962  4 21 13:34 out_v26/bw_near_200077.jpg
-rw-r--r--  1 kondoh  staff   27857  4 21 13:31 out_v26/bw_near_200076.jpg
-rw-r--r--  1 kondoh  staff   27951  4 21 13:28 out_v26/bw_near_200075.jpg
-rw-r--r--  1 kondoh  staff   27738  4 21 13:25 out_v26/bw_near_200074.jpg
-rw-r--r--  1 kondoh  staff   27942  4 21 13:22 out_v26/bw_near_200073.jpg
-rw-r--r--  1 kondoh  staff   27636  4 21 13:19 out_v26/bw_near_200072.jpg

0,1,2,... ときて 81の処理のどこかで起きてるようなので...

10 * 81 / 300 = 2.7

これで再現するだろうか

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v26/dbg div=2 use_srv srv_c use_img_srv init_sec=12.7
OpenCV: FFMPEG: tag 0x34363248/'H264' is not supported with codec id 28 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31637661/'avc1'
frm : 0/300(0.0%) : not start yet
wh : 0/76800(0.0%) : not start yet
Traceback (most recent call last):
  File "./img_sock.py", line 158, in 
    f()
  File "./img_sock.py", line 137, in col
    col = img.col(*lst)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 139, in col
    return videos.col(fn, sec, x, y, def_col, rep)
  File "/Users/kondoh/rt_wk/rt_v26/img.py", line 102, in col
    return img_sec(fn, sec)[y, x]
TypeError: 'NoneType' object is not subscriptable

再現する

追いかけてみるとどうも

v26.patch

diff -urN rt_v25/img.py rt_v26/img.py
  :
-	video = []
-	while vc.isOpened():
-		(ret, img) = vc.read()
-		if not ret:
-			break
-		video.append(img)
-	n = len(video)
  :
+	kd = { 'i': 1, 'w': 3, 'h': 4, 'fps': 5, 'n': 7 }
+	get_prop = lambda k: vc.get( kd.get(k) )
+	set_prop = lambda k, v: vc.set( kd.get(k), v )
+	fix_type = lambda k, v: int(v) if k != 'fps' else v
+
+	dic = dict( map( lambda k: ( k, fix_type( k, get_prop(k) ) ), kd.keys() ) )
+
+	fps = dic.get('fps')
+	n = dic.get('n')
  :

ここで以前は動画ファイルに含まれるフレームの数nを、 最後までreadしみて決定してましたが、、、

今回のv26では、vc.get(7)で返る結果を使うように変更してました。

さらに、以前は最初に全フレームをreadして保持してましたが、 さすがにメモリを使い過ぎる気がして、 必要なときに vc.set(1, xxx) でseekしてreadするよう変更してました。

どうも、取得したフレーム数 n の範囲内でも、 read に失敗する現象が出てるようです。 (IMG_3999_3.mov の 171 フレームの read で失敗してます)

というか、以前は最初にまとめてreadして、 失敗した段階でそこまでのフレーム数を、動画ファイルのフレームと思っていた。

というか、というか、今回 vc.get(7) で取得するようにした値は、 動画ファイルに含まれるフレーム数として正しい値じゃなかった!?

動画ファイルを画像ファイルに展開してみて、
どこかでエラーがでるか試してみます。

$ ./img.py
Usage: ./img.py img_name video_name fps= zm= cmd=
  fps is int value (default 30)
  zm is float value (default 1.0)
  cmd is v2i, i2v or play (default i2v)

$ ./img.py out_v26/for_dbg_3 IMG_3999_3.mov cmd=v2i
Traceback (most recent call last):
  File "./img.py", line 263, in 
    video_to_imgs(img_name, video_name)
  File "./img.py", line 233, in video_to_imgs
    cv2.imwrite(fn, imgs[i] )
TypeError: 'map' object is not subscriptable
bash-3.2$ 

って、これは違うエラー
python3 対応もれ...

とりあえず

img.py
  :
def read_video(fn):
  :
	#dic['imgs'] = lambda : map( img_idx, range(n) ) # func
	dic['imgs'] = lambda : ut.map_lst( img_idx, range(n) ) # func

で修正して
次のパッチに入れるとして


$ ./img.py out_v26/for_dbg_3 IMG_3999_3.mov cmd=v2i

エラーはでませんな...

ls -l out_v26/for_dbg_3*
-rw-r--r--  1 kondoh  staff  79995  4 22 07:58 out_v26/for_dbg_300000.jpg
-rw-r--r--  1 kondoh  staff  78942  4 22 07:58 out_v26/for_dbg_300001.jpg
-rw-r--r--  1 kondoh  staff  78168  4 22 07:58 out_v26/for_dbg_300002.jpg
  :
-rw-r--r--  1 kondoh  staff  66253  4 22 07:58 out_v26/for_dbg_300168.jpg
-rw-r--r--  1 kondoh  staff  64499  4 22 07:58 out_v26/for_dbg_300169.jpg
-rw-r--r--  1 kondoh  staff  67107  4 22 07:58 out_v26/for_dbg_300170.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300171.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300172.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300173.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300174.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300175.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300176.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300177.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300178.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300179.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300180.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300181.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300182.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300183.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300184.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300185.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300186.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300187.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300188.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300189.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300190.jpg
-rw-r--r--  1 kondoh  staff      0  4 22 07:58 out_v26/for_dbg_300191.jpg

ほれー!
171フレーム以降が 0 バイトになってる

さてどうしたものか

$ python
Python 3.6.5 (default, Mar 30 2018, 06:41:49) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> import cv2

>>> vc = cv2.VideoCapture('IMG_3999_3.mov')

>>> vc.get(7)
192.0

>>> list(map( vc.get, range(8) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0]

>>> list(map( vc.get, range(9) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0]

>>> list(map( vc.get, range(10) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0, 0.0]

>>> list(map( vc.get, range(11) ))
[0.0, 0.0, 0.0016666666666666668, 640.0, 360.0, 28.64246643460965, 828601953.0, 192.0, 0.0, 0.0, 0.0]

確かに192を返してて、他に171を返すようなプロパティは無さそうですね...

全部メモリに保持するのは避けるとして、 時間はかかりますが、最初に空読みしてread可能なフレーム数だけ調べる対応にしてみますかな。

img.py

  :
def read_video(fn):
	vc = cv2.VideoCapture(fn)

	kd = { 'i': 1, 'w': 3, 'h': 4, 'fps': 5, 'n': 7 }
	get_prop = lambda k: vc.get( kd.get(k) )
	set_prop = lambda k, v: vc.set( kd.get(k), v )
	fix_type = lambda k, v: int(v) if k != 'fps' else v

	dic = dict( map( lambda k: ( k, fix_type( k, get_prop(k) ) ), kd.keys() ) )

	fps = dic.get('fps')
	n = dic.get('n')

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
  :
		if 'img' not in dic:
			( ret, dic['img'] ) = vc.read()
			if not ret:
				sys.stderr.write( 'err i/n={}/{}\n'.format( dic.get('i'), dic.get('n') ) )
		return dic.get('img')

これならどうか? 解像度を落としてエラーチェック。

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
  :
wh : 300/300(100.0%) : fin 0.80s
frm : 65/300(21.7%) : total 4m 8.20s : rest 3m 14.42s : 2018/04/22 09:52:53
wh : 0/300(0.0%) : not start yet
wh : 300/300(100.0%) : fin 0.80s
wh : 0/300(0.0%) : not start yet
err i/n=171/178
Traceback (most recent call last):
  File "./img_sock.py", line 158, in 
    f()
  File "./img_sock.py", line 137, in col
    col = img.col(*lst)
  File "/Users/kondoh/rt_wk/rt_v27/img.py", line 149, in col
    return videos.col(fn, sec, x, y, def_col, rep)
  File "/Users/kondoh/rt_wk/rt_v27/img.py", line 112, in col
    return img_sec(fn, sec)[y, x]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):

vc.get(7)のプロパティは192が返って、 最初にまとめてread()すると177まで成功するので178フレーム分あると思ってたら、 seekしてやり読み直してると171のreadでエラー!?。

	n = dic.get('n')

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)

seekで先頭に戻してもう一度最初からreadしてみると...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
  :
err i/n=171/178
  :

n=178表示は1回しか出ないので、2回目の171もreadできてる様子?

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	for i in [171, 170, 172]:
		set_prop('i', i)
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

さらに171にseekしてread、170にseekしてread、172にseekしてreadを試すと...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
171 ret=False
170 ret=True
172 ret=False
  :

連続してreadすると177までOKで、seekしてreadするときは170まではOKとかなのだろうか?

  :
	for i in [171, 170, 172]:
		set_prop('i', i)
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 170)
	for i in range(3):
		i += 170
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

170read成功のあと、seekなしで連続でreadしてみると...

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
171 ret=False
170 ret=True
172 ret=False
170 ret=True
171 ret=False
172 ret=False

いちどerrが出てしまうと、もうだめとか?

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	set_prop('i', 0)
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			sys.stderr.write( 'n={}\n'.format(n) )
			break
	#for i in [171, 170, 172]:
	#	set_prop('i', i)
	#	(ret, _) = vc.read()
	#	sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 170)
	for i in range(3):
		i += 170
		(ret, _) = vc.read()
		sys.stderr.write( '{} ret={}\n'.format(i, ret) )
	set_prop('i', 0)

いかに?

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
n=178
170 ret=True
171 ret=False
172 ret=False

seek 0のあとは178までread OKで、 seek 170すると171でNG ? 確認

$ python
>>> import cv2
>>> vc = cv2.VideoCapture('IMG_3999_3.mov')
>>> vc.set(1, 170)
True
>>> vc.read()
(True, array([[[ 10,  15,  15],
        [ 10,  15,  15],
        [ 10,  15,  15],
        ...,
        [ 21,  24,  29],
  :
>>> vc.get(1)
171.0
>>> (ret,_) = vc.read()
>>> ret
False

>>> vc.set(1, 0)
True
>>> vc.get(1)
0.0

>>> for i in range(169):
... 	(ret, _) = vc.read()
... 

>>> (ret, _) = vc.read()
>>> ret
True
>>> (ret, _) = vc.read()
>>> ret
True
>>> (ret, _) = vc.read()
>>> ret
True
>>> vc.get(1)
172.0

確かに、0から連続してreadしてる分には171もread OK

さてどうしたものか。

最初連続readで上限を取得しておいて、 つかってて途中でエラーでたら、 seek 0で先頭からreadしてなんとかするとか。

	def err_recover():
		if vc.isOpened():
			vc.open(fn)
			if 'img' in dic:
				dic.pop('img')
		if get_prop('i') != 0:
			set_prop('i', 0)

	# get_prop(7) is doubt
	for i in range(n):
		(ret, _) = vc.read()
		if not ret or len(_) == 0:
			dic['n'] = n = i
			break
	err_recover()

	def img_idx(i):
		i %= n # loop
		if 'img' in dic and dic.get('i') != i:
			dic.pop('img')
			if dic.get('i')+1 != i:
				set_prop('i', i)
			dic['i'] = i
		if 'img' not in dic:
			( ret, dic['img'] ) = vc.read()
			if not ret:
				err_recover()
				for j in range(i+1):
					( ret, dic['img'] ) = vc.read()
					if not ret:
						sys.stderr.write( 'err {} {}/{}\n'.format( fn, j, dic.get('n') ) )
				dic['i'] = i
		return dic.get('img')
	dic['img_idx'] = img_idx

これでトライ

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_32 div=32 use_srv srv_c use_img_srv init_sec=10
  :
wh : 300/300(100.0%) : fin 0.67s
frm : 300/300(100.0%) : fin 3m 38.28s

一応エラーではとまらなくなりました。

パッチを作っておいて。

v27.patch

それでは、前回のリベンジ。

$ ./cg.py eyep=[0,0,0],50,20 sec=10 data_name=ball_world name=out_v27/bw_near_2 div=2 use_srv srv_c use_img_srv init_sec=10
  :
wh : 76530/76800(99.6%) : total 2m 44.76s : rest 0.57s : 2018/04/23 04:08:46
wh : 76800/76800(100.0%) : fin 2m 44.74s
frm : 300/300(100.0%) : fin 14h 22m 30.00s

とりあえずエラーでとまらずに10秒の動画を生成できました。 14時間半。画像サーバに切り出す前は確か11時間半。

では、いつものデータで速度比較。

まず従来方式

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2 n=1 init_sec=5 div=2
  :
wh : 76800/76800(100.0%) : fin 7m 45.41s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 7m 31.17s
  v25  wh : 76800/76800(100.0%) : fin 7m 50.96s
  v24  wh : 76800/76800(100.0%) : fin 7m 44.42s
  v23  wh : 76800/76800(100.0%) : fin 7m 48.33s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_is n=1 init_sec=5 div=2 use_img_srv
  :
wh : 76800/76800(100.0%) : fin 8m 19.96s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 8m 12.84s

レイトレ用のサーバプロセス分離版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_s n=1 init_sec=5 div=2 use_srv
  :
wh : 76800/76800(100.0%) : fin 9m 18.49s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 9m 22.96s
  v25  wh : 76800/76800(100.0%) : fin 9m 34.91s
  v24  wh : 76800/76800(100.0%) : fin 9m 38.24s
  v23  wh : 76800/76800(100.0%) : fin 9m 9.07s

$ rm xdat

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_s_is n=1 init_sec=5 div=2 use_srv use_img_srv
  :
wh : 76800/76800(100.0%) : fin 9m 53.40s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 9m 54.44s

$ rm xdat

レイトレ用のサーバプロセスをC言語で実装版

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_sc n=1 init_sec=5 div=2 use_srv srv_c
  :
wh : 76800/76800(100.0%) : fin 53.66s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 53.40s
  v25  wh : 76800/76800(100.0%) : fin 53.95s
  v24  wh : 76800/76800(100.0%) : fin 54.07s
  v23  wh : 76800/76800(100.0%) : fin 1m 50.29s

イメージ用のサーバプロセスを使う場合は

$ ./cg.py eyep=[0,0,0],200,10 sec=10 data_name=objs name=out_v27/objs_1_2_sc_is n=1 init_sec=5 div=2 use_srv srv_c use_img_srv
  :
wh : 76800/76800(100.0%) : fin 1m 8.39s

  以前の
  v26  wh : 76800/76800(100.0%) : fin 1m 6.02s

まぁ、変わらず。