Skip to content

Commit

Permalink
Merge 18187bd into 798fa89
Browse files Browse the repository at this point in the history
  • Loading branch information
digitaltom committed Jul 3, 2018
2 parents 798fa89 + 18187bd commit f991249
Show file tree
Hide file tree
Showing 58 changed files with 255 additions and 67 deletions.
4 changes: 3 additions & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
2.1.2
2.3.3
# ruby version 2.3.3 from raspbian stretch
# to compile it on a more recent linux see: https://github.com/rbenv/ruby-build/wiki#openssl-usrincludeopensslasn1_mach102-error-error-this-file-is-obsolete-please-update-your-software
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: ruby
rvm:
- 2.1.5
- 2.3.3
sudo: required
addons:
chrome: stable
Expand All @@ -9,3 +9,6 @@ script:
- 'bundle exec rubocop -S'

bundler_args: --without production

after_script:
- bundle exec rake coveralls:push
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'rails', '~> 4.2'
Expand Down Expand Up @@ -46,5 +48,6 @@ group :test do
gem 'chromedriver-helper'
gem 'coveralls', require: false
gem 'rspec-rails'
gem 'simplecov'
gem 'simplecov', require: false
gem 'simplecov-console', require: false
end
9 changes: 8 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ GEM
tilt
angular_rails_csrf (3.0.0)
railties (>= 3, < 5.2)
ansi (1.5.0)
archive-zip (0.11.0)
io-like (~> 0.3.0)
arel (6.0.4)
Expand Down Expand Up @@ -86,6 +87,7 @@ GEM
haml (5.0.4)
temple (>= 0.8.0)
tilt
hirb (0.7.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
io-like (0.3.0)
Expand Down Expand Up @@ -184,6 +186,10 @@ GEM
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-console (0.4.2)
ansi
hirb
simplecov
simplecov-html (0.10.2)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -237,10 +243,11 @@ DEPENDENCIES
rspec-rails
rubocop
simplecov
simplecov-console
sprockets-rails (~> 2.3.3)
sqlite3
uglifier (>= 1.3.0)
unicorn-rails

BUNDLED WITH
1.16.1
1.16.2
23 changes: 11 additions & 12 deletions INSTALL-RASPBIAN.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
##Installation of the photobooth software on a raspberry pi 3 with raspbian:
## Prepare a Raspberry Pi 3 with Raspbian for Photobox setup:

- Download and install raspbian: https://www.raspberrypi.org/downloads/raspbian/
- Boot raspbian (connected to monitor and keyboard) and set up ssh for easy access (default login: pi:raspberry)
- Use `raspi-config` to set the password and other settings
- Change to root user with `sudo su`
- Set your timezone: `dpkg-reconfigure tzdata`
- Setup Wifi: https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md
- Install needed packages: `sudo apt-get install vim ruby git imagemagick time sqlite3 libsqlite3-dev nodejs libssl-dev gphoto2`
- Clone the photobooth repo: `cd /root; git clone https://github.com/digitaltom/photobooth.git`
- Install the needed gems: `cd photobooth; bundle install`
- Precompile the assets: `RAILS_ENV=production rake assets:precompile`
- Autostart the app on reboot: `cp /root/photobooth/photobooth.service /etc/systemd/system/photobooth.service; systemctl enable /etc/systemd/system/photobooth.service`
(the minimal image is enough)
- Install to SD card: https://www.raspberrypi.org/documentation/installation/installing-images/README.md

- Boot raspbian (connected to monitor and keyboard) and login (default login: pi:raspberry)

Anything is not working when following this manual? Please open an issue in the github project!
or

do the setup *headless*: https://hackernoon.com/raspberry-pi-headless-install-462ccabd75d0
- Use `sudo raspi-config` to set the password and timezone
- Setup Wifi: https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md
- Install needed packages: `sudo apt-get install vim ruby ruby-dev git imagemagick time sqlite3 gphoto2 libssl-dev nodejs libsqlite3-dev`
100 changes: 80 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,93 @@

# Photobooth

This application is supposed to run on a linux machine which is connected to a gphoto supported camera.
I've build it to run on a raspberry pi 2 with openSUSE, connected to a Nikon D60 camera.
It also runs on a raspberry pi 3 with raspbian, see INSTALL file for an installation manual.
This application is supposed to run on a linux machine which is connected to a (gphoto)[http://www.gphoto.org/] supported camera ([list](http://www.gphoto.org/proj/libgphoto2/support.php)).

The Angular.js frontend uses a Rails server on the backend to trigger and process the pictures.
Because this is a webapp, any tablet with a web-browser connected to the same wifi as the raspi
I've build it to run on a Raspberry Pi with openSUSE/Raspbian, connected to a Nikon D60 camera. See below for install instructions.

The *Angular.js* frontend uses a *Ruby on Rails* server on the backend to trigger and process the pictures.
Any tablet or notebook with a web-browser connected to the same wifi as the raspi
will work as a screen.

LEDs can get connected to the raspberry pi's gpio ports.
It uses port 23 for 'ready', port 4 for 'picture 1', port 5 for 'picture 2',
port 6 for 'picture 3', port 17 for 'picture 4' and port 24 for 'image processing'.
LEDs can get connected to the Raspberry Pi's gpio ports.
It uses port 23 for 'ready', the ports 4,5,6,17 for picture 1-4 and port 24 for 'image processing'.

By default, the UI runs in read-only mode, so that you can share the url with the users,
that are connected to the same wifi. So they can directly download and share the picture
with their mobile phone.
By default, the UI runs in read-only mode (no '*take a picture*' and '*delete*' buttons), so that you can share the url with the users,
that are connected to the same wifi. So they can directly download and share the pictures
with their mobile phones.

To load the UI in record mode (with 'take picture' button), access it with: http://<ip>/?rw/#/
To load the UI in record mode, open it like this: http://&lt;ip&gt;/?rw/

## Photobox pictures:
## Hardware Setup

As a wedding box:
The general hardware setup looks like this:

![wedding_box](https://user-images.githubusercontent.com/582520/32445572-765e1e0a-c306-11e7-92b4-99331baf6092.png)
```
+--------------+
| |
| Camera |
| |
+------^-------+
|
USB Cable |
| gphoto lib
+--------------+ +------v------------+
| Tablet | Wifi | Photobooth server |
| /Notebook +-------------> | (eg. Raspberry Pi)|
| with Browser | | |
+--------------+ +------+------------+
| gpio ports
Wires |
|
+------v-------+
| |
| Status LEDS |
| |
+--------------+
```

## Server Setup

The Photobooth can run on any Linux server, for building a portable photo booth I recommend running it on a Raspberry Pi.

General instructions on how to install Rasbian on the Raspberry can be found in [INSTALL-RASPBIAN.md](INSTALL-RASPBIAN.md)

## Software Setup

- Clone the photobooth repo:
- `sudo su`
- `cd /root; git clone https://github.com/digitaltom/photobooth.git`
- Install the needed gems:
- `echo 'gem: --no-document' >> ~/.gemrc`
- `gem install bundler`
- `cd photobooth; bundle install`
- Precompile the assets: `RAILS_ENV=production rake assets:precompile`
- Autostart the app on boot time:
- `cp photobooth.service /etc/systemd/system/photobooth.service`
- `systemctl enable /etc/systemd/system/photobooth.service`
- `cp config/options.yml config/options-local.yml` and set your config options in config/options-local.yml

## Operations

## Prototype pictures:
Useful commands to run the photobooth

![alt tag](https://raw.github.com/digitaltom/photobooth/master/public/images/readme/box_front.jpg)
![alt tag](https://raw.github.com/digitaltom/photobooth/master/public/images/readme/internals.jpg)
![alt tag](https://raw.github.com/digitaltom/photobooth/master/public/images/readme/picture_list.jpg)
![alt tag](https://raw.github.com/digitaltom/photobooth/master/public/images/readme/picture_list2.jpg)
- Control the app with systemd:
`systemctl <start|stop|restart|status> photobooth`
- See the logfile: `tail -f log/production.log`
- Rake tasks
- `rake picture_set:record`: Trigger a new picture from console
- `rake picture_set:recreate_polaroid_images[path]`: Re-create all polaroid images in a batch
- `rake picture_set:recreate_animations[path]`: Re-create all animations in a batch
- `rake picture_set:export[output,path]`: Export all images into one output directory

Anything is not working when following this manual? Please open an [issue](https://github.com/digitaltom/photobooth/issues) in the github project!


## My Photobooth:

My current photobox setup is a Raspberry Pi 3, a Nikon D60 with a Nikkor 35mm lens and a Nexus 7 tablet. All build into an old wooden suitcase.
It can run completely from battery for 2-3 hours.

It was already used at multiple parties and weddings.

![wedding_box](https://user-images.githubusercontent.com/582520/32445572-765e1e0a-c306-11e7-92b4-99331baf6092.png)
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ photoBoothApp.controller('PictureSetCtrl', [
$scope.loadPictureSet()

$scope.rw = (location.search != '?rw/');
$scope.send_by_email = window.send_by_email;

}]);
5 changes: 2 additions & 3 deletions app/assets/templates/picture_set.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
%b Last <

#button-all
%a.btn.btn-default.btn-lg{ 'ng-href' => "#/picture_set/{{picture_set.date}}/email" }
%b Send per email
%br
%a.btn.btn-default.btn-lg{ 'ng-show' => 'send_by_email', 'ng-href' => "#/picture_set/{{picture_set.date}}/email" }
%b Send by email
%a.btn.btn-default.btn-lg{ 'ng-href' => "#/picture_set/{{picture_set.date}}/gallery" }
%b Single pictures

Expand Down
2 changes: 1 addition & 1 deletion app/assets/templates/picture_set_email.html.haml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.container.mail-container
.row
.col-sm-12
%p.lead.text-center
%h2.text-center
Get your picture send to you immediately:
.row
.col-sm-10
Expand Down
2 changes: 1 addition & 1 deletion app/assets/templates/picture_set_gallery.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
.row.text-center
%a.btn.btn-default.btn-lg{ 'ng-href' => "#/picture_set/{{picture_set.date}}" }
%b Back
%a.btn.btn-default.btn-lg{ 'ng-href' => "#/picture_set/{{picture_set.date}}/email" }
%a.btn.btn-default.btn-lg{ 'ng-show' => 'send_by_email', 'ng-href' => "#/picture_set/{{picture_set.date}}/email" }
%b Send per email
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from Exception, with: :handle_exception
Expand Down
5 changes: 4 additions & 1 deletion app/controllers/picture_sets/emails_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# frozen_string_literal: true

module PictureSets
class EmailsController < ApplicationController

def create
picture_set = PictureSet.find(params[:picture_set_id])
Thread.new do
t = Thread.new do
::PictureSetMailer.image_email(params[:email], picture_set).deliver_now
end
t.abort_on_exception = true
render json: ''
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/picture_sets_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

class PictureSetsController < ApplicationController
protect_from_forgery with: :exception

Expand Down
2 changes: 2 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# frozen_string_literal: true

module ApplicationHelper
end
2 changes: 2 additions & 0 deletions app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

class ApplicationMailer < ActionMailer::Base
default from: OPTS.mail_settings['from']
layout 'mailer'
Expand Down
9 changes: 5 additions & 4 deletions app/mailers/picture_set_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# frozen_string_literal: true

class PictureSetMailer < ApplicationMailer

def image_email(email, picture_set)
image_path = Rails.root.join('public', picture_set[:path])
attachments.inline['animation.gif'] = File.read(File.join(image_path, picture_set[:animation]))
if File.exist?(File.join(image_path, picture_set[:combined]))
attachments.inline['combined.jpg'] = File.read(File.join(image_path, picture_set[:combined]))
attachments.inline['animation.gif'] = File.read(File.join(picture_set.dir, picture_set.animation))
if File.exist?(File.join(picture_set.dir, picture_set.combined))
attachments.inline['combined.jpg'] = File.read(File.join(picture_set.dir, picture_set.combined))
end
I18n.with_locale(OPTS.locale) do
mail(to: email, subject: I18n.t('picture_set_mailer.image_email.subject', name: OPTS.image_caption))
Expand Down
2 changes: 2 additions & 0 deletions app/models/gpio_port.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# We need to unexport the pins manually here, waiting for merge of:
# https://github.com/jwhitehorn/pi_piper/pull/32

Expand Down
24 changes: 13 additions & 11 deletions app/models/picture_set.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# frozen_string_literal: true

class PictureSet

DATE_FORMAT = '%Y-%m-%d_%H-%M-%S'.freeze
POLAROID_SUFFIX = '_polaroid.png'.freeze
ANIMATION_SUFFIX = '_animation.gif'.freeze
COMBINED_SUFFIX = '_combined.jpg'.freeze
DATE_FORMAT = '%Y-%m-%d_%H-%M-%S'
POLAROID_SUFFIX = '_polaroid.png'
ANIMATION_SUFFIX = '_animation.gif'
COMBINED_SUFFIX = '_combined.jpg'
PICTURE_PATH = Rails.root.join('public', 'picture_sets').to_s

attr_accessor :date, :dir, :animation, :combined, :pictures, :next, :last
Expand All @@ -22,7 +24,7 @@ def find(date)
all_sets = PictureSet.all
ps = all_sets.detect { |set| set.date == date }
raise 'PictureSet not found' unless ps
ps.next = all_sets[all_sets.index(ps) - 1] if all_sets.index(ps) > 0
ps.next = all_sets[all_sets.index(ps) - 1] if all_sets.index(ps).positive?
ps.last = all_sets[all_sets.index(ps) + 1] if all_sets.index(ps) < all_sets.size
ps
end
Expand All @@ -36,7 +38,7 @@ def create(date: Time.now.getlocal.strftime(DATE_FORMAT))
# wait until convert jobs are finished
until jobs.none?(&:status) do end
picture_set.create_animation
picture_set.combine_images
picture_set.combine_images if OPTS.render_collage
(1..4).each { |i| GpioPort.off(GpioPort::GPIO_PORTS["PICTURE#{i}"]) }
GpioPort.off(GpioPort::GPIO_PORTS['PROCESSING'])
picture_set
Expand Down Expand Up @@ -89,7 +91,7 @@ def destroy
def convert_to_polaroid(num, angle)
caption = OPTS.image_caption || date
Syscall.execute("time convert -caption '#{caption}' #{date}_#{num}.jpg " \
'-sample 600 ' \
'-scale 600 ' \
'-bordercolor Snow ' \
'-density 100 ' \
'-gravity center ' \
Expand All @@ -105,23 +107,23 @@ def create_animation(overwrite: false)
Rails.logger.info "Skipping for existing animation #{dir}"
else
Rails.logger.info "Creating animation for #{dir}"
Syscall.execute("time convert -delay 60 #{date}_{1..4}#{POLAROID_SUFFIX} #{animation}", dir: dir)
Syscall.execute("time convert -delay 60 #{date}_[1-4]#{POLAROID_SUFFIX} #{animation}", dir: dir)
end
end

# Create collage of all images
def combine_images(overwrite: false)
if File.exist?(File.join(dir, combined)) && !overwrite
Rails.logger.info "Skipping for existing collage #{dir}"
Rails.logger.info "Skipping for collage creation for #{dir}"
else
Rails.logger.info "Creating collage for #{dir} in background thread"
Rails.logger.info "Creating collage for #{dir}"
Syscall.execute("time montage -geometry '25%x25%+25+25<' " \
"-background '#{OPTS.background_color}' " \
"-title '#{OPTS.image_caption}' " \
"-font '#{OPTS.font}' " \
"-fill '#{OPTS.font_color}' " \
"-pointsize #{OPTS.combined_image_fontsize} " \
"-gravity 'Center' #{date}_{1..4}.jpg " \
"-gravity 'Center' #{date}_[1-4].jpg " \
"#{date}#{COMBINED_SUFFIX}", dir: dir)
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/syscall.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'open3'

class Syscall
Expand Down

0 comments on commit f991249

Please sign in to comment.