Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI improvements - autodiscover previews images, is more robust #27

Merged
merged 7 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading