Skip to content

Commit

Permalink
CLI improvements - autodiscover previews images, is more robust (#27)
Browse files Browse the repository at this point in the history
* Preview continues past first error.

* Autodiscover previews images, and is more robust.

* cleaning up exception handling a bit.

* Bumping version to 0.4.5

* Automatically reformatting code with black and isort

* Woops - failed to fully refactor.

---------

Co-authored-by: Auto-format Bot <[email protected]>
  • Loading branch information
robotrapta and Auto-format Bot authored Feb 9, 2024
1 parent 0549bb8 commit fa5e071
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 44 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "framegrab"
version = "0.4.4"
version = "0.4.5"
description = "Easily grab frames from cameras or streams"
authors = ["Groundlight <[email protected]>"]
license = "MIT"
Expand Down
36 changes: 27 additions & 9 deletions src/framegrab/cli/autodiscover.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import traceback

import click
import yaml
from imgcat import imgcat

from framegrab import FrameGrabber
from framegrab.cli.clitools import PREVIEW_COMMAND_CHOICES, preview_image


@click.command()
def autodiscover():
"""Automatically Discover cameras connected to the current host (e.g. USB)."""
@click.argument(
"preview",
type=click.Choice(PREVIEW_COMMAND_CHOICES, case_sensitive=False),
default="imgcat",
)
def autodiscover(preview: str):
"""Automatically discover cameras connected to the current host (e.g. USB)."""
# Print message to stderr
click.echo("Discovering cameras...", err=True)

Expand All @@ -18,13 +27,22 @@ def autodiscover():

# Get a frame from each camera
for camera_name, grabber in grabbers.items():
frame = grabber.grab()

click.echo(f"Grabbed frame from {camera_name} with shape {frame.shape}", err=True)
click.echo(grabber.config, err=True)
yaml_config["image_sources"].append(grabber.config)

grabber.release()
try:
frame = grabber.grab()

yaml_config["image_sources"].append(grabber.config)
if frame is None:
click.echo(f"Failed to grab sample frame from {camera_name}.", err=True)
continue

click.echo(f"Grabbed sample frame from {camera_name} with shape {frame.shape}", err=True)
click.echo(grabber.config, err=True)
preview_image(frame, camera_name, preview)

grabber.release()
except Exception:
click.echo(f"Error while setting up {camera_name}.", err=True)
click.echo(traceback.format_exc(), err=True)

# render the yaml config dict as yaml and print it
click.echo("Rendering sample configuration file as YAML:\n", err=True)
Expand Down
54 changes: 54 additions & 0 deletions src/framegrab/cli/clitools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import ascii_magic
import cv2
from imgcat import imgcat
from PIL import Image


def imgcat_preview(name: str, frame):
"""Displays the given frame in the terminal using imgcat."""
print(f"Previewing image from camera {name} in terminal. This requires an advanced terminal like iTerm2.")
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
imgcat(frame_rgb)


def cv2_preview(name: str, frame):
"""Displays the given frame in a cv2 window, and wait for a key."""
cv2.imshow(name, frame)
print(f"Previewing image in cv2 window. Select the window and press any key to continue.")
_ = cv2.waitKey(0)
cv2.destroyAllWindows()


def ascii_preview(name: str, frame):
"""Displays the given frame in the terminal using ascii art."""
columns, _ = shutil.get_terminal_size()
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(frame_rgb)
out = ascii_magic.from_pillow_image(pil_image)
out.to_terminal(columns=columns)


def null_preview(name: str, frame):
"""Does nothing."""
pass


_PREVIEW_COMMANDS = {
"imgcat": imgcat_preview,
"cv2": cv2_preview,
"ascii": ascii_preview,
"none": null_preview,
}

PREVIEW_COMMAND_CHOICES = list(_PREVIEW_COMMANDS.keys())


def preview_image(frame, title: str, output_type: str):
"""Displays the given frame using the given output method."""
if output_type not in PREVIEW_COMMAND_CHOICES:
raise ValueError(f"Invalid output method: {output_type}. Valid options are {PREVIEW_COMMAND_CHOICES}.")
command = _PREVIEW_COMMANDS[output_type]
if frame is None:
print(f"Trying to preview None frame from {title}.")
return
command(title, frame)
48 changes: 14 additions & 34 deletions src/framegrab/cli/preview.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import shutil
import traceback

import ascii_magic
import click
Expand All @@ -8,30 +9,7 @@
from PIL import Image

from framegrab import FrameGrabber


def imgcat_preview(name: str, frame):
"""Displays the given frame in the terminal using imgcat."""
print(f"Previewing image from camera {name} in terminal. This requires an advanced terminal like iTerm2.")
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
imgcat(frame_rgb)


def cv2_preview(name: str, frame):
"""Displays the given frame in a cv2 window, and wait for a key."""
cv2.imshow(name, frame)
print(f"Previewing image in cv2 window. Select the window and press any key to continue.")
_ = cv2.waitKey(0)
cv2.destroyAllWindows()


def ascii_preview(name: str, frame):
"""Displays the given frame in the terminal using ascii art."""
columns, _ = shutil.get_terminal_size()
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(frame_rgb)
out = ascii_magic.from_pillow_image(pil_image)
out.to_terminal(columns=columns)
from framegrab.cli.clitools import PREVIEW_COMMAND_CHOICES, preview_image


def get_image_sources_from_config(config: str) -> list:
Expand All @@ -47,7 +25,7 @@ def get_image_sources_from_config(config: str) -> list:
@click.argument("config", type=click.Path(exists=True))
@click.argument(
"output",
type=click.Choice(["imgcat", "cv2", "ascii"], case_sensitive=False),
type=click.Choice(PREVIEW_COMMAND_CHOICES, case_sensitive=False),
default="imgcat",
)
def preview(config: str, output: str):
Expand All @@ -57,18 +35,20 @@ def preview(config: str, output: str):
image_sources = get_image_sources_from_config(config)
grabbers = FrameGrabber.create_grabbers(image_sources)

preview_command = {
"imgcat": imgcat_preview,
"cv2": cv2_preview,
"ascii": ascii_preview,
}[output]

try:
# Get a frame from each camera
for camera_name, grabber in grabbers.items():
frame = grabber.grab()
print(f"Grabbed frame from {camera_name}")
preview_command(camera_name, frame)
try:
frame = grabber.grab()
except Exception as e:
print(f"Error grabbing preview frame from {camera_name}: {e}")
traceback.print_exc()
continue
if frame is None:
print(f"Failed to grab preview frame from {camera_name}.")
continue
print(f"Grabbed preview frame from {camera_name}")
preview_image(frame, camera_name, output)
finally:
for grabber in grabbers.values():
try:
Expand Down

0 comments on commit fa5e071

Please sign in to comment.