VR360 на 4-х Raspberri Pi


Raspberry
          Pi

Камера для съемки сферического видео практически ничем не отличается от таковой для съемки сферических панорам. Разница только в программах. Причем, как выяснилось, разница в возможностях принципиальная. Для данного проекта была собрана новая камера, хотя для экспериментов вполне можно было использовать и модель, описанную в статье Съемка панорам с квадракоптера 4 камерами Raspberry Pi или еще ранее в статье Панорамная камера на 4-х Raspberry Pi. Новым в конструкции было использование 3D принтера для создания всех ее деталей и для минимизации размеров использование модели A+.

RaspberryPi

RaspberryPi

Для желающих модернизировать схему выкладываю исходный файл проекта fritzing.

Основное отличие видеосъемки от фотосъемки в том, что простого способа обеспечить одинаковые экспозиционные параметры для всех камер не существует. Можно зафиксировать выдержку, в данной конструкции это обеспечивается тремя перемычками, которые изменяют выдержку соответственно в 2, 4 и 8 раз. Но есть проблема с фиксацией чувствительности. При видеосъемке она определяется аналоговым усилением, которое можно зафиксировать при достижении определенного значения. Однако если мы включим камеры при полном отсутствии света, то нарастание происходит так быстро, что не удается зафиксировать минимальное значение и в результате кадры получаются пересвеченными.  Вероятно, самый правильный подход это обеспечить при включении избыток света и потом медленно его уменьшать пока параметр   camera.analog_gain <= 1 не превысит единицу. Ниже приведем простейший вариант программы для испытания возможности съемки 3D видео.

import RPi.GPIO as GPIO
import time
import picamera
fr=0
flag1=0
GPIO.setmode(GPIO.BCM)
GPIO.setup(24, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(23, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(22, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(27, GPIO.IN, pull_up_down = GPIO.PUD_UP)
f = open('/home/pi/fotopicam/foto.txt', 'r')
fs = f.read()
f.close()
ffs = fs +'f'
f = open('/home/pi/fotopicam/foto.txt', 'w')
f.write(ffs)
f.close()
with picamera.PiCamera() as camera:
	#camera.resolution = (2592, 1944)
	camera.resolution = (1296, 972)
	camera.framerate = 25
	while camera.analog_gain <= 1:
		#time.sleep(0.01)
		a=1
	camera.exposure_mode = "off"
	camera.preview_fullscreen=0
	camera.preview_window = (0,0,1024,768)
	#camera.crop = (0,0,1,1)
	#camera.contrast = -9
	#camera.drc_strength = 'low'
	#camera.rotation = 180
	#camera.awb_mode = "sunlight"
	camera.awb_mode = "off"
	camera.awb_gains =1.3
	camera.iso = 100
	bp=4*GPIO.input(23) + 2*GPIO.input(22) + 1*GPIO.input(27)
	print(bp)
	sp=200*2**bp
	print(sp)		
	camera.shutter_speed = sp
	#camera.shutter_speed = 0
	camera.start_preview()
	start = time.time()
	def printFunction(channel):
		print("Button 1 pressed!")
		global start
		fin = time.time()
		print fin - start
		if fin-start>4:
			start = time.time()
			global fr
			fr=fr+1
			bp=4*GPIO.input(23) + 2*GPIO.input(22) + 1*GPIO.input(27)
			print(bp)
			sp=200*2**bp
			print(sp)		
			camera.shutter_speed = sp
			#camera.capture("/home/pi/fotopicam/"+ffs+"pir%03d.jpg" % fr)
			camera.start_recording('/home/pi/fotopicam/'+ffs+'v189d%02d.h264' % fr)
			camera.wait_recording(20)
			camera.stop_recording()

		
	GPIO.add_event_detect(24, GPIO.FALLING, callback=printFunction, bouncetime=300)
	while True:
		a=1
	
GPIO.cleanup()

Таким образом, камера позволяет получить 4 ролика в формате  h264 с частотой 25 - 40 кадров и разрешением 1296 х 972 пикселей. К сожалению, 5Мп камера не лучший выбор для видеосъемки. Необходимость получить частоту кадров большую 25 кадров в секунду со всего, а не с части кадра  вынуждает использовать не оптимальный размер кадра, да и желанной на современном этапе  частоты в 60 кадров в секунду достичь не удается. Для видеосъемке лучше бы подошли 2 Мп камеры, но  их для raspberry pi нет.  Тем не менее и из кадров  1296 х 972 можно сшить результирующую панораму, подходящую для создания 4K фильма.

RaspberryPi

Для получения панорамы необходимо сперва преобразовать фильм в серию кадров. Это можно сделать, например, с помощью программы  Avidemux. Однако, прямо файлы, записанные raspberry pi,  этот видеоредактор есть отказывается. Надо, не перекодируя поток, заменить шапку файла. Это можно сделать  командой:

$ ffmpeg -i ffv189d09.h264 -vcodec copy d.mp4

После этого берем 4 кадра с 4-х разных камер  и сшиваем их в программе hugin. После этого полученный pto файл размножаем по числу кадров в фильме, меняя нумерацию исходных файлов и производной панорамы.  Я для этого написал простенькую программу на python:

i=1

while i<500:
	print i
	f = open('k%04d.pto' % i, 'w')
	f.write('# hugin project file\n')
	f.write('#hugin_ptoversion 2\n')
	f.write('p f2 w3616 h1808 v360  E-1 R0 n"TIFF_m c:LZW r:CROP"\n')
	f.write('m g1 i0 f0 m2 p0.00784314\n')
	f.write('\n')
	f.write('# image lines\n')
	f.write('#-hugin  cropFactor=8.88889\n')
	f.write('i w1296 h972 f3 v132.79092133175 Ra0 Rb0 Rc0 Rd0 Re0 Eev0 Er1 Eb1 r91.7334710344494 p35.2355068741324 y90.7113404448069 TrX0 TrY0 TrZ0 Tpy0 Tpp0 j0 a-0.0779258290171618 b0.187300404446539 c-0.158813475348347 d-7.39236309218826 e-21.2187708682985 g0 t0 Va1 Vb0 Vc0 Vd0 Vx0 Vy0  Vm5 n"a%04d.jpg"\n' % i)
	f.write('#-hugin  cropFactor=8.88889\n')
	f.write('i w1296 h972 f3 v=0 Ra=0 Rb=0 Rc=0 Rd=0 Re=0 Eev0 Er1 Eb1 r0.265504905384904 p12.1541606390608 y177.08648752991 TrX0 TrY0 TrZ0 Tpy0 Tpp0 j0 a=0 b=0 c=0 d=0 e=0 g=0 t=0 Va=0 Vb=0 Vc=0 Vd=0 Vx=0 Vy=0  Vm5 n"b%04d.jpg"\n' % i)
	f.write('#-hugin  cropFactor=8.88889\n')
	f.write('i w1296 h972 f3 v=0 Ra=0 Rb=0 Rc=0 Rd=0 Re=0 Eev0 Er1 Eb1 r-91.1708737844503 p33.4848210019912 y-90.9717219324534 TrX0 TrY0 TrZ0 Tpy0 Tpp0 j0 a=0 b=0 c=0 d=0 e=0 g=0 t=0 Va=0 Vb=0 Vc=0 Vd=0 Vx=0 Vy=0  Vm5 n"d%04d.jpg"\n' % i)
	f.write('#-hugin  cropFactor=8.88889\n')
	f.write('i w1296 h972 f3 v=0 Ra=0 Rb=0 Rc=0 Rd=0 Re=0 Eev0 Er1 Eb1 r-2.54092850931036 p6.18891274760264 y-1.52951382781939 TrX0 TrY0 TrZ0 Tpy0 Tpp0 j0 a=0 b=0 c=0 d=0 e=0 g=0 t=0 Va=0 Vb=0 Vc=0 Vd=0 Vx=0 Vy=0  Vm5 n"c%04d.jpg"\n' % i)
	f.write('# specify variables that should be optimized\n')
	f.write('\n')
	f.write('\n')
	f.write('v v0\n')
	f.write('v Ra0\n')
	f.write('v Rb0\n')
	f.write('v Rc0\n')
	f.write('v Rd0\n')
	f.write('v Re0\n')
	f.write('v r0\n')
	f.write('v p0\n')
	f.write('v y0\n')
	f.write('v a0\n')
	f.write('v b0\n')
	f.write('v c0\n')
	f.write('v d0\n')
	f.write('v e0\n')
	f.write('v Vb0\n')
	f.write('v Vc0\n')
	f.write('v Vd0\n')
	f.write('v Eev1\n')
	f.write('v r1\n')
	f.write('v p1\n')
	f.write('v y1\n')
	f.write('v Eev2\n')
	f.write('v r2\n')
	f.write('v p2\n')
	f.write('v y2\n')
	f.write('v Eev3\n')
	f.write('v\n')
	f.write('\n')
	f.write('\n')
	f.write('# control points\n')
	f.write('c n0 N1 x1235.24083149664 y127.257610779813 X141 Y888 t0\n')
	f.write('c n1 N2 x1237.00001024178 y468.000012029162 X412 Y160 t0\n')
	f.write('c n1 N2 x1245.30561913907 y784.490359964262 X137 Y165 t0\n')
	f.write('c n1 N2 x1191.52473716681 y440.603101521289 X455 Y120 t0\n')
	f.write('c n1 N2 x1172.38871673525 y284.353139096614 X586 Y172 t0\n')
	f.write('c n2 N3 x534 y871 X214 Y321 t0\n')
	f.write('c n2 N3 x325.000012806543 y804.000009220578 X90 Y501 t0\n')
	f.write('c n2 N3 x472.000011141474 y709.000009712193 X54 Y297 t0\n')
	f.write('c n2 N3 x174.000013975759 y827.000009102336 X114 Y680 t0\n')
	f.write('c n2 N3 x77.0000138310751 y788.000009671966 X108 Y786 t0\n')
	f.write('c n2 N3 x193.754463789521 y911.408667213434 X191 Y681 t0\n')
	f.write('c n2 N3 x499 y919 X243 Y383 t0\n')
	f.write('c n3 N0 x1235 y553 X984.000008088705 Y762.000011791949 t0\n')
	f.write('c n3 N0 x1216.36444762422 y704.521915078927 X1115 Y770 t0\n')
	f.write('c n0 N1 x737.490539820278 y247.485329020132 X84 Y278 t0\n')
	f.write('c n0 N1 x608.418830456787 y45.4888719409063 X313 Y280 t0\n')
	f.write('c n0 N1 x640.510618692054 y292.503126589897 X114 Y156 t0\n')
	f.write('c n0 N1 x957.000012561647 y182.000009331734 X63 Y552 t0\n')
	f.write('c n0 N1 x755.000011103022 y213.00000972772 X101 Y315 t0\n')
	f.write('c n1 N2 x791.460180471985 y54.4471318025001 X990 Y109 t0\n')
	f.write('c n1 N2 x1124.0000086752 y623.000012097523 X311 Y21 t0\n')
	f.write('c n1 N2 x846.000012828683 y88.0000090526702 X928 Y99 t0\n')
	f.write('c n2 N3 x474.000011401505 y748.000009617286 X87 Y317 t0\n')
	f.write('c n2 N3 x558.491705225592 y691.485856461498 X83.4429229148175 Y199.360330694823 t0\n')
	f.write('c n2 N3 x531.000012302507 y901.000009381274 X239 Y340 t0\n')
	f.write('c n3 N0 x1160.00001048018 y443.000011319827 X864 Y843 t0\n')
	f.write('c n3 N0 x1127.00001262955 y245.000009924376 X686 Y796 t0\n')
	f.write('c n3 N0 x1209.00001050738 y444.000011646311 X886 Y792 t0\n')
	f.write('c n3 N0 x1087.00000986201 y502.000011190117 X887 Y927 t0\n')
	f.write('c n3 N0 x1120.00001317784 y200.000009577058 X649 Y775 t0\n')
	f.write('c n3 N0 x1278.00000998119 y487.000012464053 X948 Y725 t0\n')
	f.write('c n3 N0 x1186 y561 X981.000007479203 Y837.000012180542 t0\n')
	f.write('c n3 N0 x1123.52227368721 y440.577950366151 X846 Y877 t0\n')
	f.write('c n3 N0 x1104.55277061141 y371.591763246737 X775 Y873 t0\n')
	f.write('c n3 N0 x920.405855994468 y146.419688910555 X481.22113874548 Y889.991148656754 t0\n')
	f.write('\n')
	f.write('#hugin_optimizeReferenceImage 3\n')
	f.write('#hugin_blender enblend\n')
	f.write('#hugin_remapper nona\n')
	f.write('#hugin_enblendOptions --gpu --dijkstra=50\n')
	f.write('#hugin_enfuseOptions \n')
	f.write('#hugin_hdrmergeOptions -m avg -c\n')
	f.write('#hugin_outputLDRBlended true\n')
	f.write('#hugin_outputLDRLayers false\n')
	f.write('#hugin_outputLDRExposureRemapped false\n')
	f.write('#hugin_outputLDRExposureLayers false\n')
	f.write('#hugin_outputLDRExposureBlended false\n')
	f.write('#hugin_outputLDRStacks false\n')
	f.write('#hugin_outputLDRExposureLayersFused false\n')
	f.write('#hugin_outputHDRBlended false\n')
	f.write('#hugin_outputHDRLayers false\n')
	f.write('#hugin_outputHDRStacks false\n')
	f.write('#hugin_outputLayersCompression LZW\n')
	f.write('#hugin_outputImageType jpg\n')
	f.write('#hugin_outputImageTypeCompression LZW\n')
	f.write('#hugin_outputJPEGQuality 90\n')
	f.write('#hugin_outputImageTypeHDR exr\n')
	f.write('#hugin_outputImageTypeHDRCompression LZW\n')
	f.write('#hugin_outputStacksMinOverlap 0.7\n')
	f.write('#hugin_outputLayersExposureDiff 0.5\n')
	f.write('#hugin_optimizerMasterSwitch 16\n')
	f.write('#hugin_optimizerPhotoMasterSwitch 21\n')
	f.close()
	i = i+1

Теперь загружаем список получившихся файлов в обработчик задач программы hugin и идем гулять. На моей машине: 
Processor    2x Intel(R) Core(TM)2 Duo CPU E8500 @ 3.16GHz
Memory    6110MB
Operating System    Netrunner 16
OpenGL Renderer    GeForce GTS 450/PCIe/SSE2
Disks
SanDisk SDSSDX12
сшивка одной панорамы занимала 10 секунд.

Объединяем получившиеся панорамы в фильм и перед нами встает последняя задача, как этот фильм с комфортом смотреть. Самое универсальное решение предлагает krpano. Их решение позволяет смотреть фильм через браузеры, как с поддержкой flash, так и HTML5. Однако программа платная, дорогая, и весь ее функционал мне не нужен, поскольку для сшивки есть hugin. Демо же версия, хотя и полнофункциональна, однако накладывает на фильм водяные знаки. Краткий поиск показал, что на ней свет клином не сошелся, и есть, как минимум, две программы с открытым исходным кодом для просмотра сферического видео с возможностью манипулирования картинкой. Они, правда, поддерживают только HTML5, причем только в той его инкарнации, которая реализована в Chrom или Chromium. Это программы Valiant360 и Slant. Для показа через интернет я уменьшил размер панорам в два раза и преобразовал h264 в webm:

$ ffmpeg -i pan1b.mp4 -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis video-1920x960.webm

Получившийся у меня 20 секундный ролик можно просмотреть через Valiant360 в Chrom и Opera или через Slant только в Chrom.

09.04.2016
Установите проигрыватель Flash

Облако тегов:
3D печать
Arduino
Raspberry Pi
Аэрофотосъемка
Байдарки
Геомеханика
История
Камеры
Макросъемка
Объективы
Освещение
Панорамы
Принадлежности
Принтеры
Программы
Сканеры
Стереосъемка
Фильтры
Фокусировка
Фотокубики
...
rss