Linux в кишені — на службі у фотографа

Так вийшло, що фотографія, це мій основний професійний вид діяльності, а програмування — хобі, яке іноді дозволяє розім'яти мозок. Крім безпосередньо для розминки мозку, програмування і допомагає в роботі. Наприклад, писав корисні штуки, такі як або або .

Нещодавно поставив собі завдання, як б ще порадувати своїх клієнтів. Згадав численні прохання клієнтів на весільну зйомку: «Як добре було б, якби на банкеті ви змогли показати коротеньке слайдшоу з фотографій, які відзняли за день». На ці прохання доводилося відмовляти, з кількох причин: лінощі тягати з собою ноутбук для складання слайдшоу, немає часу на відбір пари десятків знімків з сотень, з raw знову ж таки потрібно конвертувати, і найголовніше — на це все потрібен час, якого немає.

Це розповідь про те, як мені вдалося зробити для себе інструмент, який з мінімальним моєю участю і мінімальним додатковим вагою в рюкзаку, допомагає зробити красиві слайдшоу. І, звичайно ж, розповідь про python, ffmpeg і linux на android.



Несподіваний вибір заліза

Перша проблема — це зайва вага. Мені потрібен був повноцінний linux на досить пристойному залозі. Спочатку мій вибір припав на Orange PI PC, про який я почув на гигтаймсе. Залізниця була замовлена і доставлена. Мені здавалося, це те що треба — 4 ядра по 1.5 ггц, 1 гб оперативної пам'яті і повноцінні USB. Але на ділі, зайвий раз переконався, що без нормальної підтримки, все «клони raspberry», нічого не варті. Дуже глючний образи OS, постійно відвалюються ядра під навантаженням, проблема з роботою бібліотек, щоб, наприклад підключити lcd дисплей.
І сама головна проблема, це несподіваний killed, при вільних 800 мб оперативки, на ділянці коду типу:
from PIL Image import
img=Image.new('RGB',(6000,4000)) #насправді мені потрібно було не створювати, а відкривати фотографії
img.rotate()

Причому теж прекрасно працювало на нетбуці з 1гб оперативці без свопу, а так само на Raspberry PI першому. І вже тим більше, і мови не могло бути, щоб робити теж саме, але на 4-х ядрах одночасно.

Рішення прийшло несподівано, коли я взяв в руки смартфон, щоб прочитати прийшло повідомлення:
А в кишені постійно лежить залізяка із 2.2 ггц 4-х ядерним процесором, 2гб оперативної пам'яті + USB otg є (Nexus 5). Залишилося знайти спосіб повноцінного запуску Linux оточення. Після відкидання різних варіантів з перепрошивкою (хотілося користуватися їм повноцінно і як смартфоном), вихід був знайдений — Linux Deploy. Якщо коротко, Linux Deploy запускає повноцінне linux оточення в chroot'e (докладніше про програму можна почитати блог у нашого співвітчизника — розробника), і найголовніше для мене — монтувати довільний каталог з fs android свого оточення. Без цього, не була б можлива робота з картрідер SD карт пам'яті, застромленим у OTG роз'єм.

Відбір фотографій

Слайдшоу з сотень фотографій, зайняло б пару годин часу. Потрібен був спосіб легко і швидко відібрати 20-40 фотографій. Перегортати навіть 100 фотографій зі смартфона — то ще задоволення, а кількість може доходити до вечора до тисячі (дублі з серійної зйомки, шлюб, пристрілювальні фото, репортаж та ін)
Поглянувши на фотоапарат, згадав про кнопку, якою ніколи не користувався — рятівницею виявилася кнопка «rate», яка присвоює рейтинг фотографії:



Колесом прокрутки праворуч досить швидко гортаються знімки, і на потрібному натискається кнопка «rate». Так як ти вже знаєш, що за вдалого сьогодні відзняв, на все йде не більше пари хвилин. Залишається змусити програму знайти і вибрати ті знімки, яким присвоєно хоч який-небудь рейтинг.

Так як рейтинг потрапляє в exif, знадобиться чудовий пакет exiftool (sudo apt-get install libimage-exiftool-perl) і wrapper до нього для python. А далі все просто:

import os
import exiftool
all_files=[]

"""проходимся по всіх файлів на SD карті """
for directory, dirnames, in filenames os.walk(PATH_TO_SD_ROOT): 
for name in filenames:
f=os.path.join(directory name)
if f.lower().endswith('.cr2') or f.lower().endswith('.jpg'): #і додаємо в список jpg і raw файли
all_files.append(f)
tool=exiftool.ExifTool() 
tool.start() #запускаємо exiftool

# просимо пройтися по нашому списку і видати рейтинги
result=tool.get_tags_batch(['XMP:Rating'],all_files)
rated_files=[]
for x in result:
if x['XMP:Rating']>0:
rated_files.append(x['SourceFile'])
# на виході отримуємо список потрібних нам файлів
rated_files.sort()


Наступний етап досить тривіальний — копіювання потрібних фотографій в тимчасовий каталог і резайс в декілька потоків для подальшої роботи. Єдине, на що хотів звернути увагу, це raw, в який я знімаю. Конвертацією займається утиліта dcraw (хоча це не повноцінна конвертація, а лише висмикування вшитою jpg в raw файл, але в даному випадку, це більш ніж достатньо.

import subprocess

for n,x in enumerate(self.rated_files):
dcraw_opts = ["dcraw", "-e", "-c", x] # -e - витягнути вшитий jpg, -з видати нам його в stdout
dcraw_proc = subprocess.Popen(dcraw_opts, stdout=subprocess.PIPE) 
image = StringIO.StringIO(dcraw_proc.communicate()[0]) # беремо фотографію зі stdout
image.seek(0)
open('input/%02d.jpg'%(n),'wb').write(image.read()) # і записуємо в потрібне місце.


Зроби мені красиво!



На попередньому етапі можна було б зупинитися, взявши фотографії і пустивши їх як слайдшоу на ноутбуці діджея, підключеного до проектора, але хочеться, щоб все це виглядало красиво.
На допомогу приходить така чудова річ, як ffmpeg (avconv). Я не любитель якихось яскравих спецефектів, мені досить легкою динаміки, у вигляді zoom'a фотографії і «crossfade» переходу між слайдами. Скажу відразу, незважаючи на величезні можливості ffmpeg, це зробити у мене не вийшло. Наприклад, фільтр zoompan, видавав жахливе якість і тремтячу картинку. Після тижні, проведені за читанням мануалів і форумів, вирішено було зробити це «в лоб»:

def processImage(numb):
img=Image.open('input_temp/%02d.jpg'%numb) # відкриваємо поточне зображення
# для ефекту crossfade відкриває наступне зображення, або якщо воно останнім, створюємо порожній
try:next_img=Image.open('input_temp/%02d.jpg'%(numb+1)).resize((1280,720),Image.ANTIALIAS)
except:next_img=Image.new('RGB',(1280,720),'black')

# щоб не захаращувати пам'ять сотнями окремих кадрів, попросимо ffmpeg приймати stdin фотографії
# і отримаємо на виході готовий відрізок відео
p = subprocess.Popen(['avconv', 'y', 'f', 'image2pipe', '-vcodec', 'mjpeg', 'r', '25', 'i', '-', '-vcodec', 'mjpeg','q:v', '3' , 'r', '25', 'output/%02d.mjpg'%(numb)], stdin=subprocess.PIPE)


# 100 кадрів при 25 к/с - 4 секунди відео на слайд
for x in xrange(100):
# при кожній ітерації робимо кроп вихідної фотографії у відповідності з пропорцією 16:9
n=img.crop((int(float(x)*16.0/9.0),x,int(1920.0-float(x)*16.0/9.0),1080-x))
# і робимо резайс до кінцевого розміру
n=n.resize((1280,720),Image.ANTIALIAS)
# на третій секунді, починаємо "підмішувати" наступну фотграфию
if $ x>75:
n=Image.blend(n,next_img,float(x-75)/25)

# і згодовуємо ffmpeg'у
n.save(p.stdin,'JPEG')
p.stdin.close()
p.wait()


Ах так, я щось там говорив про ядра процесора. Хотілося б распаралеллить цей процес, щоб були зайняті всі ядра. В python це робиться дуже просто:
from multiprocessing import Pool
s=len(glob.glob('input_temp/*.jpg')) #беремо кількість фотографій 
pool = Pool()
pool.map(processImage, xrange(s)) # і віддаємо їх воркерам, кількість яких дорівнює кількості ядер процесора
pool.close()
pool.join()


У результаті ми маємо безліч відрізків mjpeg відео, які потрібно з'єднати воєдино, вставивши музику.
Погугливши, не знайшов кращого способу, як спочатку безпосередньо з'єднати відео, використовуючи cat:
cat 00.mjpg 01.mjpg ..... > out.mjpg

Залишилося тільки переконвертувати його в потрібний формат, додавши музику:
avconv -threads 4 -framerate 25 -i out.mjpg -i audio.mp3 -shortest -y -r 25 -preset veryfast out.mp4


Щоб не возитися кожен раз в консолі, а потрібно було вибирати музичний трек, вписувати назву для слайдшоу (для першого кадру) і пр, підняв простий web-сервер, який стартує при запуску Linux Deploy. Я використовував простенький фреймворк bottle. Виглядає це ось так:



Разом

2-3 хвилини відбір фотографій, запуск Linux Deploy, localhost в браузері, пару секунд на те, щоб вписати title і натиснути на СТАРТ. Далі 10-15 хвилин роботи смартфона, і відео готове:



Таким же способом можна робити не тільки слайдшоу з фотографій, але і склеювати відео: позначити потрібні уривки в камері кнопкою rate і склеїти їх потім ffmpeg'ом.

І невеликий анонс
Якщо ця тема виявиться цікавою, я зроблю ще кілька публікацій. Наприклад, на черзі стаття, як зробити ось таку милу і функціональну фотобудку:


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.