141 lines
3.9 KiB
Python
Executable File
141 lines
3.9 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
|
|
import argparse
|
|
import sys
|
|
import logging
|
|
import cv2
|
|
import numpy as np
|
|
from pathlib import Path
|
|
from inspect import getmembers, isfunction
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def apply_filter_bw(image):
|
|
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
|
|
return image
|
|
|
|
|
|
def apply_filter_grain(image):
|
|
noise = np.random.randint(0, 256, image.shape, dtype="uint8")
|
|
noise = cv2.cvtColor(noise, cv2.COLOR_BGR2GRAY)
|
|
noise = cv2.cvtColor(noise, cv2.COLOR_GRAY2BGR)
|
|
scale = 0.8
|
|
image = cv2.addWeighted(image, scale, noise, 1 - scale, 0)
|
|
return image
|
|
|
|
|
|
def main() -> int:
|
|
# Handle program arguments
|
|
ap = argparse.ArgumentParser(
|
|
prog="filmsim", description="Experimental film effect simulator"
|
|
)
|
|
ap.add_argument(
|
|
"--mode",
|
|
help="Mode to operate in (image or video)",
|
|
default="video",
|
|
choices=["image", "video"],
|
|
)
|
|
ap.add_argument(
|
|
"--input",
|
|
help="Path to the input image or video (uses webcam if unset)",
|
|
type=Path,
|
|
)
|
|
ap.add_argument(
|
|
"--output",
|
|
help="Path to the output image or video (displays output if unset)",
|
|
type=Path,
|
|
)
|
|
ap.add_argument(
|
|
"--filters", help="Comma-separated list of filters to apply", default="bw,grain"
|
|
)
|
|
ap.add_argument(
|
|
"-v", "--verbose", help="Enable verbose logging", action="store_true"
|
|
)
|
|
args = ap.parse_args()
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
format="%(levelname)s: %(message)s",
|
|
)
|
|
|
|
# Build filter chain
|
|
filter_functions = getmembers(
|
|
sys.modules[__name__],
|
|
lambda member: isfunction(member)
|
|
and member.__name__.startswith("apply_filter_"),
|
|
)
|
|
enabled_filters = args.filters.split(",")
|
|
filters = [
|
|
filter_function
|
|
for filter_name, filter_function in filter_functions
|
|
if filter_name.replace("apply_filter_", "") in enabled_filters
|
|
]
|
|
|
|
# Read input image or video
|
|
if args.mode == "image":
|
|
if args.input is None:
|
|
webcam = cv2.VideoCapture(0)
|
|
ret, image = webcam.read()
|
|
if not ret:
|
|
logger.error("Failed to capture image from webcam")
|
|
return 1
|
|
else:
|
|
image = cv2.imread(str(args.input))
|
|
if image is None:
|
|
logger.error("Failed to read input image")
|
|
return 1
|
|
|
|
# Apply filters
|
|
for filter_function in filters:
|
|
image = filter_function(image)
|
|
|
|
# Write output image
|
|
if args.output is None:
|
|
cv2.imshow("Output", image)
|
|
cv2.waitKey(0)
|
|
else:
|
|
cv2.imwrite(str(args.output), image)
|
|
|
|
elif args.mode == "video":
|
|
if args.input is None:
|
|
webcam = cv2.VideoCapture(0)
|
|
else:
|
|
webcam = cv2.VideoCapture(str(args.input))
|
|
if not webcam.isOpened():
|
|
logger.error("Failed to open input video")
|
|
return 1
|
|
|
|
# Read video frame by frame
|
|
while True:
|
|
ret, frame = webcam.read()
|
|
if not ret:
|
|
logger.error("Failed to read frame from video")
|
|
return 1
|
|
|
|
# Apply filters
|
|
for filter_function in filters:
|
|
frame = filter_function(frame)
|
|
|
|
# Write output frame
|
|
if args.output is None:
|
|
# Flip frame if using webcam
|
|
if args.input is None:
|
|
frame = cv2.flip(frame, 1)
|
|
cv2.imshow("Output", frame)
|
|
if cv2.waitKey(1) & 0xFF == ord("q"):
|
|
break
|
|
# else:
|
|
# cv2.imwrite(str(args.output), frame)
|
|
|
|
webcam.release()
|
|
cv2.destroyAllWindows()
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|