commit 35492a05a2979076ed44203ff3f9de28068f26cb Author: puckprutt Date: Sun Mar 1 23:53:30 2026 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83658ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.git +images +__pycache__ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/context.py b/context.py new file mode 100644 index 0000000..7174beb --- /dev/null +++ b/context.py @@ -0,0 +1,53 @@ +from collections.abc import MutableMapping + +class PP_Context(MutableMapping): + + def __init__(self, *args, **context): + self.store = dict() + self.update(dict(*args, **context)) + + def get(self, thing): + return self.store.get(thing) + + def get_or_set(self, thing, this): + if self._has(thing): + return self.get(thing) + self.set(thing, this) + return self.get(thing) + + def get_or_false(self, thing): + if self._has(thing): + ret = self.get(thing) + if not ret is None: + return ret + return False + + def set(self, thing, value): + self.__setitem__(thing, value) + + def _has(self, thing): + if (isinstance(thing, str) or isinstance(thing, int)) and thing in self.store: + return True + return False + + def __getitem__(self, key): + return self.store[self.__keytransform__(key)] + + def __setitem__(self, key, value): + self.store[self.__keytransform__(key)] = value + setattr(self, self.__keytransform__(key), value) + + def __delitem__(self, key): + del self.store[self.__keytransform__(key)] + delattr(self, self.__keytransform__(key)) + + def __iter__(self): + return iter(self.store) + + def __len__(self): + return len(self.store) + + def __keytransform__(self, key): + # :D + return key.lower() + diff --git a/frame.py b/frame.py new file mode 100644 index 0000000..1b12e62 --- /dev/null +++ b/frame.py @@ -0,0 +1,58 @@ +from context import PP_Context +import cv2 as vision + +class PP_Frame_Context(PP_Context): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._frame_init_value() + + def _frame_init_value(self): + self.get_or_set("moviefile", 0) + self.get_or_set("width", None) + self.get_or_set("height", None) + self.get_or_set("savename", "saved") + self.get_or_set("color", True) + self.get_or_set("hide", False) + self.get_or_set("run", True) + self.get_or_set("current_frame", None) + +class PP_Frame(object): + + def __init__(self, frame, context=None): + if isinstance(context, PP_Frame_Context): + self.context = context + elif isinstance(context, dict): + self.context = PP_Frame_Context(context) + else: + self.context = PP_Frame_Context() + + self.color = self.context.get_or_set("color", True) + self.blur = self.context.get_or_set("gblur", False) + + self.frame_rgb = frame + self.frame_gray = vision.cvtColor(frame, vision.COLOR_BGR2GRAY) + + if self.blur: + self._init_gblur() + + @property + def data(self): + if self.color: + return self.frame_rgb + return self.frame_gray + + def gaussian_blur(self, ksize=(5,5), sigmaX=0, dst=None, sigmaY=None, borderType=None, hint=None): + if self.color: + self.frame_rgb = vision.GaussianBlur(self.frame_rgb, (5,5), 0, dst=dst, sigmaY=sigmaY, borderType=borderType, hint=hint) + self.frame_gray = vision.GaussianBlur(self.frame_gray, (5,5), 0, dst=dst, sigmaY=sigmaY, borderType=borderType, hint=hint) + + def _init_gblur(self): + self.gaussian_blur( + ksize=self.context.get_or_set("gblur_ksize", (5,5)), + sigmaX=self.context.get_or_set("gblur_sigmaX", 0), + dst=self.context.get_or_set("gblur_dst", None), + sigmaY=self.context.get_or_set("gblur_sigmaY", None), + borderType=self.context.get_or_set("gblur_borderType", None), + hint=self.context.get_or_set("gblur_hint", None) + ) + diff --git a/pp_vision.py b/pp_vision.py new file mode 100644 index 0000000..ad24d37 --- /dev/null +++ b/pp_vision.py @@ -0,0 +1,153 @@ +import sys +import cv2 as vision +from pathlib import Path +from datetime import datetime +from frame import PP_Frame +from frame import PP_Frame_Context + +# make the folder of this file the root folder. +ROOT_DIR = Path(__file__).parent + +# make ROOT_DIR/images the default save folder. +SAVE_DIR = ROOT_DIR / "images" + +# create folders and parent folders in case they dont exist. +SAVE_DIR.mkdir(parents=True, exist_ok=True) + +def take_photo(width, height, color=True, filename=None): + # select camera device + camera = vision.VideoCapture(0) + + #set width and height + if not isinstance(width, int) or not isinstance(height, int): + print("width and height needs to be integer") + camera.set(3,width) + camera.set(4,height) + + #take picture + _, picture = camera.read() + picture = Frame(picture, color) + camera.release() + + #save picture if filename is sent as a parameter. + if isinstance(filename, str): + vision.imwrite(str(ROOT_DIR / "images" / f"{filename}.png"), picture.data) + + return picture + +def show_picture(picture, title="Kuken"): + while True: + key = vision.waitKey(1) & 0xFF + vision.imshow(title, picture) + if key == ord('q'): + vision.destroyAllWindows() + break + elif key == ord('s'): + if not title.endswith(".png"): + title = f"{title}.png" + vision.imwrite(title, picture) + print(f"saved: {title}.png") + vision.destroyAllWindows() + break + return + +"""function start_video() + +@Param width : int [required] +----------------------------- + width for video stream to use + +@Param height : int [required] +------------------------------ + height for video stream to use + +@Param color : bool [default: True] +----------------------------------- + True will leave colors on + False will display grayscale. + +@Param filename : str [default: "saved"] +---------------------------------------- + when saving a picture it will be saved as {filename}_{year}-{month}-{day}_{timme}_{minut}.png + +@Param camera : int | str [default: 0] +-------------------------------------- + camera device by int or filename to a video. + +@Param callback : function [default: None] +------------------------------------------ + a callable function that will manipulate the picture accordingly. + +@Returns status : bool +---------------------- + True if successfully opened a videostream + False if it failed to open a videostream. +""" +def start_video(ctx: PP_Frame_Context = None, callback=None): + if ctx is None: + ctx = PP_Frame_Context() + + #select camera device + camera = vision.VideoCapture(ctx.moviefile) + + #set width and height of picture + if isinstance(ctx.width, int) and isinstance(ctx.height, int): + camera.set(3,ctx.width) + camera.set(4,ctx.height) + + if not camera.isOpened(): + print("could not open camera stream :(") + return False + + while ctx.run: + # get picture from camera. + success, frame = camera.read() + frame = PP_Frame(frame, ctx) + if ctx.get_or_false("current_frame"): + ctx.set("current_frame", ctx["current_frame"] + 1) + key = vision.waitKey(1) & 0xFF + + # gracefull quit if camera feed dies or 'q' is pressed + if not success or key == ord('q'): + ctx.run = False + continue + # save image if 's' is pressed + elif key == ord('s'): + now = datetime.now().strftime("%Y-%m-%d_%H_%M") + vision.imwrite(f"{ctx.savename}_{now}.png", frame.data) + print(f"saved: {ctx.savename}_{now}.png") + # show frame + elif key == ord('v'): + now = datetime.now().strftime("%Y-%m-%d_%H_%M") + show_picture(frame.data, title=f"{ctx.savename}_{now}") + elif key == ord('c'): + ctx.set("color", not ctx.color) + elif key == ord('g'): + if ctx._has("gblur"): + ctx.set("gblur", not ctx.gblur) + else: + ctx.set("gblur", True) + elif key == ord('h'): + hide = ctx.get_or_set("hide", False) + ctx.set("hide", not ctx.hide) + if ctx.hide: + print("hide new frames..") + + # run callback function if it is callable + if not callback is None and callable(callback): + try: + frame = callback(frame, context) + except Exception: + pass + + if not ctx.get_or_false("hide"): + vision.imshow(f"{ctx.savename} | puckoprutt", frame.data) + + vision.destroyAllWindows() + camera.release() + return True + +if __name__ == '__main__': + #pic = take_photo(640, 480, color=False, filename=None) + #show_picture(pic) + vid = start_video(640, 480, color=True)