Skip to content
This repository has been archived by the owner on Aug 27, 2022. It is now read-only.

Commit

Permalink
takePic.php simplify usage of cameracontrol.py
Browse files Browse the repository at this point in the history
cameracontrol.py add live chromakeying with option to use chromakeying for saved image (currently replaces non chromakeyed image), fix possible endless loop of graceful exit if no camera is connected
  • Loading branch information
up-87 authored and andi34 committed Feb 22, 2022
1 parent 8d61e53 commit 9ed9a50
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 41 deletions.
134 changes: 102 additions & 32 deletions api/cameracontrol.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env python

import signal
import sys
import time
Expand All @@ -10,6 +12,33 @@


class CameraControl:
def __init__(self, args):
self.running = True
self.args = args
self.showVideo = True
self.chroma = {}
self.camera = None
self.socket = None
self.ffmpeg = None

signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)

self.connect_to_camera()

if args.imgpath is not None:
try:
self.capture_image(args.imgpath)
if args.chroma_sensitivity is not None and args.chroma_sensitivity > 0:
self.handle_chroma_params(args)
self.chroma_key_image(args.imgpath)
sys.exit(0)
except gp.GPhoto2Error as e:
print('An error occured: %s' % e)
sys.exit(1)
else:
self.pipe_video_to_ffmpeg_and_wait_for_commands()

def connect_to_camera(self):
try:
self.camera = gp.Camera()
Expand Down Expand Up @@ -86,6 +115,7 @@ def handle_message(self, msg):
if args.exit:
self.socket.send_string('Exiting service!')
self.exit_gracefully()
self.handle_chroma_params(args)
if args.device != self.args.device:
self.args.device = args.device
self.ffmpeg_open()
Expand All @@ -97,6 +127,10 @@ def handle_message(self, msg):
if args.imgpath is not None:
try:
self.capture_image(args.imgpath)
print('chroma')
if args.chroma_sensitivity is not None and args.chroma_sensitivity > 0:
print('do chroma')
self.chroma_key_image(args.imgpath)
self.socket.send_string('Image captured')
if self.args.bsm:
self.disable_video()
Expand All @@ -115,44 +149,59 @@ def handle_message(self, msg):
except gp.GPhoto2Error:
self.socket.send_string('failure')

def exit_gracefully(self, *args):
print('Exiting...')
if self.camera:
self.disable_video()
self.camera.exit()
print('Closed camera connection')
sys.exit(0)
def ffmpeg_open(self):
input_chroma = []
filters = []
if self.chroma.get('active', False):
filters, input_chroma = self.get_chroma_ffmpeg_params()
input_gphoto = ['-i', '-', '-vcodec', 'rawvideo', '-pix_fmt', 'yuv420p']
ffmpeg_output = ['-preset', 'ultrafast', '-f', 'v4l2', self.args.device]
self.ffmpeg = Popen(['ffmpeg', *input_chroma, *input_gphoto, *filters, *ffmpeg_output], stdin=PIPE)

def __init__(self, args):
self.args = args
self.showVideo = True
self.camera = None
self.socket = None
self.ffmpeg = None
def handle_chroma_params(self, args):
chroma_color = args.chroma_color or self.chroma.get('color', '0xFFFFFF')
chroma_image = args.chroma_image or self.chroma.get('image')
chroma_sensitivity = float(args.chroma_sensitivity or self.chroma.get('sensitivity', 0.0))
if chroma_sensitivity < 0.0 or chroma_sensitivity > 1.0:
chroma_sensitivity = 0.0
chroma_blend = float(args.chroma_blend or self.chroma.get('blend', 0.0))
if chroma_blend < 0.0:
chroma_blend = 0.0
elif chroma_blend > 1.0:
chroma_blend = 1.0
chroma_active = chroma_sensitivity != 0.0 and chroma_image is not None
print('chromakeying active: %s' % chroma_active)
self.chroma = {
'active': chroma_active,
'image': chroma_image,
'color': chroma_color,
'sensitivity': str(chroma_sensitivity),
'blend': str(chroma_blend)
}

signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def get_chroma_ffmpeg_params(self):
input_chroma = ['-i', self.chroma['image']]
filters = ['-filter_complex', '[0:v][1:v]scale2ref[i][v];' +
'[v]colorkey=%s:%s:%s:[ck];[i][ck]overlay' %
(self.chroma['color'], self.chroma['sensitivity'], self.chroma['blend'])]
return filters, input_chroma

self.connect_to_camera()

if self.args.imgpath is not None:
try:
self.capture_image(self.args.imgpath)
except gp.GPhoto2Error as e:
print('An error occured: %s' % e)
sys.exit(1)
else:
self.pipe_video_to_ffmpeg_and_wait_for_commands()

def ffmpeg_open(self):
self.ffmpeg = Popen(
['ffmpeg', '-i', '-', '-vcodec', 'rawvideo', '-pix_fmt', 'yuv420p', '-f', 'v4l2', self.args.device],
stdin=PIPE)
def chroma_key_image(self, path):
input_chroma = []
filters = []
if self.chroma.get('active', False):
filters, input_chroma = self.get_chroma_ffmpeg_params()
input_gphoto = ['-i', path]
tmp_path = "%s-chroma.jpg" % path
ffmpeg_output = [tmp_path]
Popen(['ffmpeg', *input_chroma, *input_gphoto, *filters, *ffmpeg_output]).wait(5)
Popen(['mv', tmp_path, path, '-f']).wait(1)

def pipe_video_to_ffmpeg_and_wait_for_commands(self):
context = zmq.Context()
self.socket = context.socket(zmq.REP)
self.socket.bind('tcp://*:5555')
self.handle_chroma_params(self.args)
self.ffmpeg_open()
try:
while True:
Expand All @@ -176,6 +225,16 @@ def pipe_video_to_ffmpeg_and_wait_for_commands(self):
except KeyboardInterrupt:
self.exit_gracefully()

def exit_gracefully(self, *_):
if self.running:
self.running = False
print('Exiting...')
if self.camera:
self.disable_video()
self.camera.exit()
print('Closed camera connection')
sys.exit(0)


class MessageSender:
def __init__(self, message):
Expand All @@ -193,6 +252,8 @@ def __init__(self, message):
except zmq.Again:
print('Message receival not confirmed')
sys.exit(1)
except KeyboardInterrupt:
print('Interrupted!')


def is_already_running():
Expand All @@ -215,9 +276,18 @@ def main():
help='CONFIGENTRY=CONFIGVALUE analog to gphoto2 cli. Not tested for all config entries!')
parser.add_argument('-c', '--capture-image-and-download', default=None, type=str, dest='imgpath',
help='capture an image and download it to the computer. If it stays stored on the camera as \
well depends on the camera config.')
well depends on the camera config')
parser.add_argument('-b', '--bsm', action='store_true', help='start preview, but quit preview after taking an \
image and wait for message to start preview again')
image and wait for message to start preview again')
parser.add_argument('--chromaImage', type=str, help='chroma key background (full path)', dest='chroma_image')
parser.add_argument('--chromaColor', type=str,
help='chroma key color (color name or format like "0xFFFFFF" for white)', dest='chroma_color')
parser.add_argument('--chromaSensitivity', type=float,
help='chroma key sensitivity (value from 0.01 to 1.0 or 0.0 to disable). \
If this is set to a value distinct from 0.0 on capture immage command chroma keying using \
ffmpeg is applied on the image and only this modified image is stored on the pc.',
dest='chroma_sensitivity')
parser.add_argument('--chromaBlend', type=float, help='chroma key blend (0.0 to 1.0)', dest='chroma_blend')
parser.add_argument('--exit', action='store_true', help='exit the service')

args = parser.parse_args()
Expand Down
6 changes: 4 additions & 2 deletions api/takePic.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ function takePicture($filename) {
imagedestroy($im);
}
} else {
$dir = dirname($filename);
chdir($dir); //gphoto must be executed in a dir with write permission
//gphoto must be executed in a dir with write permission for other commands we stay in the api dir
if (substr($config['take_picture']['cmd'], 0, strlen('gphoto')) === 'gphoto') {
chdir(dirname($filename));
}
$cmd = sprintf($config['take_picture']['cmd'], $filename);
$cmd .= ' 2>&1'; //Redirect stderr to stdout, otherwise error messages get lost.

Expand Down
10 changes: 5 additions & 5 deletions faq/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,15 @@ Yes you can. There's different ways depending on your needs and personal setup:
<hr>

### How to get better performance using gphoto2 as preview?
By now the DSLR handling of Photobooth was done exclusively using `gphoto2 CLI` (command line interface). When taking pictures while using preview video from the same camera one command has to be stopped and another one is run after that.
By now the DSLR handling of Photobooth on Linux was done exclusively using `gphoto2 CLI` (command line interface). When taking pictures while using preview video from the same camera one command has to be stopped and another one is run after that.
The computer terminates the connection to the camera just to reconnect immediately. Because of that there was an ugly video gap and the noises of the camera could be irritating as stopping the video sounded very similar to taking a picture. But most cameras can shoot quickly from live-view...
The underlying libery of `gphoto2 CLI` is `libgphoto` and it can be accessed using several programming languages. Because of this we can have a python script that handles both preview and taking pictures without terminating the connection to the camera in between.

To try using `gphoto-python` first execute `install-gphoto-python.sh` from the Photobooth installation subdirectory `gphoto`.
```
bash gphoto/install-gphoto-python.sh
```
After that just change your commands to use the python script. For Live preview use: *(assuming your Photobooth install is at `var/www/html/`)*
After that just change your commands to use the python script. For Live preview use:
```
python3 cameracontrol.py
```
Expand All @@ -438,11 +438,11 @@ If you want to keep your images on the camera you need to use the same `capturet
```
python3 cameracontrol.py --set-config capturetarget=1
```
If you don't want to use the DSLR view as background video enable the respective setting of Photobooth add `--bsm` to the preview command. The preview video is activated when the countdown for a photo starts and after taking a picture the video is deactivated while waiting the next photo.
If you don't want to use the DSLR view as background video enable the respective setting of Photobooth and add `--bsm` to the preview command. The preview video is activated when the countdown for a photo starts and after taking a picture the video is deactivated while waiting for the next photo.

If you get errors from Photobooth and want to get more information try to run the preview command manually. The script is in Photobooth's api folder. To do so end all running services that potentially try to access the camera with `killall gphoto2` and `killall python3` (if you added any other python scripts manually you might have to be a bit more selective than this command).
If you get errors from Photobooth and want to get more information try to run the preview command manually. The script is in Photobooth's `api` folder. To do so end all running services that potentially try to access the camera with `killall gphoto2` and `killall python3` (if you added any other python scripts manually you might have to be a bit more selective than this command).

Finally if you just run `python3 cameracontrol.py --capture-image-and-download %s` as take picture command without having a preview started it only takes a picture without starting any kind of preview and ends the script immediately after the picture. In theory `cameracontrol.py` might be able to completely replace `gphoto2 CLI` for all DSLR connection handling in the future.
Finally if you just run `venv/bin/python3 cameracontrol.py --capture-image-and-download %s` as take picture command without having a preview started it only takes a picture without starting any kind of preview and ends the script immediately after the picture. In theory `cameracontrol.py` might be able to completely replace `gphoto2 CLI` for all DSLR connection handling in the future.

But by now this was not tested with distinct setups and different cameras... so feel free to give feedback!

Expand Down
4 changes: 2 additions & 2 deletions gphoto/install-gphoto-python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ if [[ $REPLY =~ ^[1]$ ]]; then
info ""
if [[ $REPLY =~ ^[Yy]$ ]]
then
info "Installing gphoto2 webcam service."
info "### Installing gphoto2 webcam service."
cp 'ffmpeg-webcam.service' '/etc/systemd/system/ffmpeg-webcam.service'
cp 'ffmpeg-webcam.sh' '/usr/ffmpeg-webcam.sh'
chmod +x '/usr/ffmpeg-webcam.sh'
Expand All @@ -68,7 +68,7 @@ if [[ $REPLY =~ ^[1]$ ]]; then
info "gphoto2 webcam service installed and running..."
fi
elif [[ $REPLY =~ ^[2]$ ]]; then
info "Stopping and removing gphoto2 webcam service."
info "### Stopping and removing gphoto2 webcam service."
systemctl stop ffmpeg-webcam.service
systemctl disable ffmpeg-webcam.service
rm '/usr/ffmpeg-webcam.sh'
Expand Down

0 comments on commit 9ed9a50

Please sign in to comment.