![]() |
ImageTk Paste - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: ImageTk Paste (/thread-33838.html) Pages:
1
2
|
ImageTk Paste - KDog - May-31-2021 I'm back again with my camera app! Trying to overlay/merge two images when saving them. The error i get is Please see code below:from tkinter import * import cv2 from PIL import Image, ImageTk, ImageEnhance import time from tkinter import filedialog import numpy as np class App: def __init__(self, video_source=0): self.overlay_img = None self.appName = "Kamera" self.window = Tk() self.window.title(self.appName) self.window.resizable(0, 0) # self.window.wm_iconbitmap("cam.ico") self.window['bg'] = 'black' self.video_source = video_source self.vid = MyVideoCapture(self.video_source) # self.label = Label(self.window, text=self.appName, font=15, bg='blue', fg='white').pack(side=TOP, fill=BOTH) self.canvas = Canvas(self.window, width=self.vid.width, height=self.vid.height, bg='red') self.canvas.pack() self.btn_snapshot = Button(self.window, text="Snapshot", width=5, command=self.snapshot) self.btn_snapshot.pack(side=LEFT, padx=10) self.btn_overlay = Button(self.window, text="Overlay", width=7, command=self.overlay) self.btn_overlay.pack(side=LEFT, padx=10) self.btn_settings = Button(self.window, text="Settings", width=5, command=self.settings) self.btn_settings.pack(side=LEFT, padx=10) self.slide_value = 200 self.update() self.window.mainloop() def settings(self): self.newWindow = Toplevel(self.window) self.newWindow.title("Settings") self.newWindow.geometry("400x400") self.btn_flip = Button(self.newWindow, text="Mirror Image", width=10, command=self.flip_img) self.btn_flip.pack(side=LEFT, padx=10) # self.brightness_lbl = Label(self.newWindow, text="Image Brightness") # self.brightness_lbl.pack(anchor=NW) #var = IntVar() self.brightness = Scale( self.newWindow, length=200, from_=0, to=255, orient=HORIZONTAL, label="Image Brightness", command=self.slide) self.brightness.set(200) self.brightness.pack(anchor=NW) def slide(self, var): self.slide_value = self.brightness.get() print(self.slide_value) def flip_img(self): self.vid.flipped = not self.vid.flipped def overlay(self): file = filedialog.askopenfile( mode='rb', defaultextension='.png',title="Choose Overlay Image", filetypes=[("PNG Files", '*.png')]) if file: self.overlay_img = ImageTk.PhotoImage(file=file) def snapshot(self): isTrue, frame = self.vid.getFrame() if isTrue: filename = filedialog.asksaveasfilename( defaultextension='.jpg', title="Choose Filename", filetypes=[("JPEG Files", '*.jpg')]) # image = "IMG-" + time.strftime("%H-%M-%S-%d-%m") + ".jpg" cv2.imwrite(filename, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # msg = Label(self.window, text='Image Saved' + image, bg='black', fg='green').place(x=430, y=510) else: messagebox.showerror("paint says", "unable to save image ,\n something went wrong") if isTrue and self.overlay_img: self.photo.paste(self.overlay_img, (0, 0)) self.stamped_img = ImageTk.PhotoImage(self.photo) filename = filedialog.asksaveasfilename( defaultextension='.jpg', title="Choose Filename", filetypes=[("JPEG Files", '*.jpg')]) filename = self.stamped_img def update(self): isTrue, frame = self.vid.getFrame() frame = cv2.normalize(frame, frame, 0, self.slide_value, cv2.NORM_MINMAX) if isTrue: self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame)) #self.canvas.tag_lower(self.photo) self.canvas.create_image(0, 0, image=self.photo, anchor=NW) if self.overlay_img: #self.canvas.tag_raise(self.overlay_img) self.canvas.create_image(0,0, image=self.overlay_img, anchor=NW) #self.stamped_img = self.photo.paste(self.overlay_img) self.window.after(100, self.update) class MyVideoCapture: def __init__(self, video_source=0): self.vid = cv2.VideoCapture(video_source) if not self.vid.isOpened(): raise ValueError("Unable to open this camera \n select another video source", video_source) self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH) self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT) self.flipped = True def getFrame(self): if self.vid.isOpened(): isTrue, frame = self.vid.read() if isTrue and self.flipped: frame = cv2.flip(frame, 1) if isTrue: return (isTrue, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) else: return (isTrue, None) else: return (isTrue, None) def __del__(self): if self.vid.isOpened(): self.vid.release() if __name__ == "__main__": App() RE: ImageTk Paste - bowlofred - May-31-2021 There is no load in your code, and you haven't included the entire traceback (which would show where the error is being produced). Is this all the code?
RE: ImageTk Paste - Yoriz - May-31-2021 Hello again, I don't see load used directly anywhere it must be called indirectly. You should show the full error traceback as it indicates where it went wrong and more information. RE: ImageTk Paste - KDog - Jun-01-2021 (May-31-2021, 10:31 PM)Yoriz Wrote: Hello again, I don't see load used directly anywhere it must be called indirectly. It's in ImageTk.py: ImageTk.py# # The Python Imaging Library. # $Id$ # # a Tk display interface # # History: # 96-04-08 fl Created # 96-09-06 fl Added getimage method # 96-11-01 fl Rewritten, removed image attribute and crop method # 97-05-09 fl Use PyImagingPaste method instead of image type # 97-05-12 fl Minor tweaks to match the IFUNC95 interface # 97-05-17 fl Support the "pilbitmap" booster patch # 97-06-05 fl Added file= and data= argument to image constructors # 98-03-09 fl Added width and height methods to Image classes # 98-07-02 fl Use default mode for "P" images without palette attribute # 98-07-02 fl Explicitly destroy Tkinter image objects # 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) # 99-07-26 fl Automatically hook into Tkinter (if possible) # 99-08-15 fl Hook uses _imagingtk instead of _imaging # # Copyright (c) 1997-1999 by Secret Labs AB # Copyright (c) 1996-1997 by Fredrik Lundh # # See the README file for information on usage and redistribution. # import tkinter from io import BytesIO from . import Image # -------------------------------------------------------------------- # Check for Tkinter interface hooks _pilbitmap_ok = None def _pilbitmap_check(): global _pilbitmap_ok if _pilbitmap_ok is None: try: im = Image.new("1", (1, 1)) tkinter.BitmapImage(data=f"PIL:{im.im.id}") _pilbitmap_ok = 1 except tkinter.TclError: _pilbitmap_ok = 0 return _pilbitmap_ok def _get_image_from_kw(kw): source = None if "file" in kw: source = kw.pop("file") elif "data" in kw: source = BytesIO(kw.pop("data")) if source: return Image.open(source) # -------------------------------------------------------------------- # PhotoImage class PhotoImage: """ A Tkinter-compatible photo image. This can be used everywhere Tkinter expects an image object. If the image is an RGBA image, pixels having alpha 0 are treated as transparent. The constructor takes either a PIL image, or a mode and a size. Alternatively, you can use the ``file`` or ``data`` options to initialize the photo image object. :param image: Either a PIL image, or a mode string. If a mode string is used, a size must also be given. :param size: If the first argument is a mode string, this defines the size of the image. :keyword file: A filename to load the image from (using ``Image.open(file)``). :keyword data: An 8-bit string containing image data (as loaded from an image file). """ def __init__(self, image=None, size=None, **kw): # Tk compatibility: file or data if image is None: image = _get_image_from_kw(kw) if hasattr(image, "mode") and hasattr(image, "size"): # got an image instead of a mode mode = image.mode if mode == "P": # palette mapped data image.load() try: mode = image.palette.mode except AttributeError: mode = "RGB" # default size = image.size kw["width"], kw["height"] = size else: mode = image image = None if mode not in ["1", "L", "RGB", "RGBA"]: mode = Image.getmodebase(mode) self.__mode = mode self.__size = size self.__photo = tkinter.PhotoImage(**kw) self.tk = self.__photo.tk if image: self.paste(image) def __del__(self): name = self.__photo.name self.__photo.name = None try: self.__photo.tk.call("image", "delete", name) except Exception: pass # ignore internal errors def __str__(self): """ Get the Tkinter photo image identifier. This method is automatically called by Tkinter whenever a PhotoImage object is passed to a Tkinter method. :return: A Tkinter photo image identifier (a string). """ return str(self.__photo) def width(self): """ Get the width of the image. :return: The width, in pixels. """ return self.__size[0] def height(self): """ Get the height of the image. :return: The height, in pixels. """ return self.__size[1] def paste(self, im, box=None): """ Paste a PIL image into the photo image. Note that this can be very slow if the photo image is displayed. :param im: A PIL image. The size must match the target region. If the mode does not match, the image is converted to the mode of the bitmap image. :param box: A 4-tuple defining the left, upper, right, and lower pixel coordinate. See :ref:`coordinate-system`. If None is given instead of a tuple, all of the image is assumed. """ # convert to blittable im.load() image = im.im if image.isblock() and im.mode == self.__mode: block = image else: block = image.new_block(self.__mode, im.size) image.convert2(block, image) # convert directly between buffers tk = self.__photo.tk try: tk.call("PyImagingPhoto", self.__photo, block.id) except tkinter.TclError: # activate Tkinter hook try: from . import _imagingtk try: if hasattr(tk, "interp"): # Required for PyPy, which always has CFFI installed from cffi import FFI ffi = FFI() # PyPy is using an FFI CDATA element # (Pdb) self.tk.interp # <cdata 'Tcl_Interp *' 0x3061b50> _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) else: _imagingtk.tkinit(tk.interpaddr(), 1) except AttributeError: _imagingtk.tkinit(id(tk), 0) tk.call("PyImagingPhoto", self.__photo, block.id) except (ImportError, AttributeError, tkinter.TclError): raise # configuration problem; cannot attach to Tkinter # -------------------------------------------------------------------- # BitmapImage class BitmapImage: """ A Tkinter-compatible bitmap image. This can be used everywhere Tkinter expects an image object. The given image must have mode "1". Pixels having value 0 are treated as transparent. Options, if any, are passed on to Tkinter. The most commonly used option is ``foreground``, which is used to specify the color for the non-transparent parts. See the Tkinter documentation for information on how to specify colours. :param image: A PIL image. """ def __init__(self, image=None, **kw): # Tk compatibility: file or data if image is None: image = _get_image_from_kw(kw) self.__mode = image.mode self.__size = image.size if _pilbitmap_check(): # fast way (requires the pilbitmap booster patch) image.load() kw["data"] = f"PIL:{image.im.id}" self.__im = image # must keep a reference else: # slow but safe way kw["data"] = image.tobitmap() self.__photo = tkinter.BitmapImage(**kw) def __del__(self): name = self.__photo.name self.__photo.name = None try: self.__photo.tk.call("image", "delete", name) except Exception: pass # ignore internal errors def width(self): """ Get the width of the image. :return: The width, in pixels. """ return self.__size[0] def height(self): """ Get the height of the image. :return: The height, in pixels. """ return self.__size[1] def __str__(self): """ Get the Tkinter bitmap image identifier. This method is automatically called by Tkinter whenever a BitmapImage object is passed to a Tkinter method. :return: A Tkinter bitmap image identifier (a string). """ return str(self.__photo) def getimage(photo): """Copies the contents of a PhotoImage to a PIL image memory.""" im = Image.new("RGBA", (photo.width(), photo.height())) block = im.im photo.tk.call("PyImagingPhotoGet", photo, block.id) return im def _show(image, title): """Helper for the Image.show method.""" class UI(tkinter.Label): def __init__(self, master, im): if im.mode == "1": self.image = BitmapImage(im, foreground="white", master=master) else: self.image = PhotoImage(im, master=master) super().__init__(master, image=self.image, bg="black", bd=0) if not tkinter._default_root: raise OSError("tkinter not initialized") top = tkinter.Toplevel() if title: top.title(title) UI(top, image).pack() RE: ImageTk Paste - Yoriz - Jun-01-2021 I think due to Namespace flooding with * imports you've learnt your lesson in why to not do it. The code is expecting a PIL Image instead I think its got a tkinter Image which has no load method see the tk Image class below.class Image: """Base class for images.""" _last_id = 0 def __init__(self, imgtype, name=None, cnf={}, master=None, **kw): self.name = None if not master: master = _get_default_root('create image') self.tk = getattr(master, 'tk', master) if not name: Image._last_id += 1 name = "pyimage%r" % (Image._last_id,) # tk itself would use image<x> if kw and cnf: cnf = _cnfmerge((cnf, kw)) elif kw: cnf = kw options = () for k, v in cnf.items(): if callable(v): v = self._register(v) options = options + ('-'+k, v) self.tk.call(('image', 'create', imgtype, name,) + options) self.name = name def __str__(self): return self.name def __del__(self): if self.name: try: self.tk.call('image', 'delete', self.name) except TclError: # May happen if the root was destroyed pass def __setitem__(self, key, value): self.tk.call(self.name, 'configure', '-'+key, value) def __getitem__(self, key): return self.tk.call(self.name, 'configure', '-'+key) def configure(self, **kw): """Configure the image.""" res = () for k, v in _cnfmerge(kw).items(): if v is not None: if k[-1] == '_': k = k[:-1] if callable(v): v = self._register(v) res = res + ('-'+k, v) self.tk.call((self.name, 'config') + res) config = configure def height(self): """Return the height of the image.""" return self.tk.getint( self.tk.call('image', 'height', self.name)) def type(self): """Return the type of the image, e.g. "photo" or "bitmap".""" return self.tk.call('image', 'type', self.name) def width(self): """Return the width of the image.""" return self.tk.getint( self.tk.call('image', 'width', self.name))Try removing from tkinter import * and using import tkinter as tk from now on.
RE: ImageTk Paste - KDog - Jun-01-2021 Hi Yoriz! I tried the above and subsequently had to add tk. before various objects such as Canvasand ButtonStill getting the same error: Could this be a potential answer (which I don't fully understand): https://stackoverflow.com/questions/40137813/attributeerror-when-creating-tkinter-photoimage-object-with-pil-imagetkMy current code: import tkinter as tk import cv2 from PIL import Image, ImageTk, ImageEnhance import time from tkinter import filedialog import numpy as np class App: def __init__(self, video_source=0): self.overlay_img = None self.appName = "Kamera" self.window = tk.Tk() self.window.title(self.appName) self.window.resizable(0, 0) # self.window.wm_iconbitmap("cam.ico") self.window['bg'] = 'black' self.video_source = video_source self.vid = MyVideoCapture(self.video_source) # self.label = Label(self.window, text=self.appName, font=15, bg='blue', fg='white').pack(side=TOP, fill=BOTH) self.canvas = tk.Canvas(self.window, width=self.vid.width, height=self.vid.height, bg='red') self.canvas.pack() self.btn_snapshot = tk.Button(self.window, text="Snapshot", width=5, command=self.snapshot) self.btn_snapshot.pack(side=tk.LEFT, padx=10) self.btn_overlay = tk.Button(self.window, text="Overlay", width=7, command=self.overlay) self.btn_overlay.pack(side=tk.LEFT, padx=10) self.btn_settings = tk.Button(self.window, text="Settings", width=5, command=self.settings) self.btn_settings.pack(side=tk.LEFT, padx=10) self.slide_value = 200 self.update() self.window.mainloop() def settings(self): self.newWindow = Toplevel(self.window) self.newWindow.title("Settings") self.newWindow.geometry("400x400") self.btn_flip = Button(self.newWindow, text="Mirror Image", width=10, command=self.flip_img) self.btn_flip.pack(side=LEFT, padx=10) # self.brightness_lbl = Label(self.newWindow, text="Image Brightness") # self.brightness_lbl.pack(anchor=NW) #var = IntVar() self.brightness = Scale( self.newWindow, length=200, from_=0, to=255, orient=HORIZONTAL, label="Image Brightness", command=self.slide) self.brightness.set(200) self.brightness.pack(anchor=NW) def slide(self, var): self.slide_value = self.brightness.get() print(self.slide_value) def flip_img(self): self.vid.flipped = not self.vid.flipped def overlay(self): file = filedialog.askopenfile( mode='rb', defaultextension='.png',title="Choose Overlay Image", filetypes=[("PNG Files", '*.png')]) if file: self.overlay_img = ImageTk.PhotoImage(file=file) def snapshot(self): isTrue, frame = self.vid.getFrame() if isTrue: filename = filedialog.asksaveasfilename( defaultextension='.jpg', title="Choose Filename", filetypes=[("JPEG Files", '*.jpg')]) # image = "IMG-" + time.strftime("%H-%M-%S-%d-%m") + ".jpg" cv2.imwrite(filename, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # msg = Label(self.window, text='Image Saved' + image, bg='black', fg='green').place(x=430, y=510) else: messagebox.showerror("paint says", "unable to save image ,\n something went wrong") if isTrue and self.overlay_img: self.photo.paste(self.overlay_img, (0, 0)) self.stamped_img = ImageTk.PhotoImage(self.photo) filename = filedialog.asksaveasfilename( defaultextension='.jpg', title="Choose Filename", filetypes=[("JPEG Files", '*.jpg')]) filename = self.stamped_img def update(self): isTrue, frame = self.vid.getFrame() frame = cv2.normalize(frame, frame, 0, self.slide_value, cv2.NORM_MINMAX) if isTrue: self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame)) #self.canvas.tag_lower(self.photo) self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW) if self.overlay_img: #self.canvas.tag_raise(self.overlay_img) self.canvas.create_image(0,0, image=self.overlay_img, anchor=tk.NW) #self.stamped_img = self.photo.paste(self.overlay_img) self.window.after(100, self.update) class MyVideoCapture: def __init__(self, video_source=0): self.vid = cv2.VideoCapture(video_source) if not self.vid.isOpened(): raise ValueError("Unable to open this camera \n select another video source", video_source) self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH) self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT) self.flipped = True def getFrame(self): if self.vid.isOpened(): isTrue, frame = self.vid.read() if isTrue and self.flipped: frame = cv2.flip(frame, 1) if isTrue: return (isTrue, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) else: return (isTrue, None) else: return (isTrue, None) def __del__(self): if self.vid.isOpened(): self.vid.release() if __name__ == "__main__": App() RE: ImageTk Paste - Yoriz - Jun-01-2021 O ![]() ![]() self.overlay_img = ImageTk.PhotoImage(file=file) self.overlay_img is a Tkinter-compatible photo imageself.photo = ImageTk.PhotoImage(image=Image.fromarray(frame)) self.photo is also a Tkinter-compatible photo imageWhen calling paste I think the passed in image has to be an actual PIL image. self.photo.paste(self.overlay_img, (0, 0)) as self.overlay_img is used in the update method You probably need to keep self.overlay_img as it isdef update(self): ... ... if self.overlay_img: #self.canvas.tag_raise(self.overlay_img) self.canvas.create_image(0,0, image=self.overlay_img, anchor=NW) Here is what I hope will fix it ![]() When you make self.overlay_img try also making a pill imagedef overlay(self): file = filedialog.askopenfile( mode='rb', defaultextension='.png',title="Choose Overlay Image", filetypes=[("PNG Files", '*.png')]) if file: self.overlay_img = ImageTk.PhotoImage(file=file) self.pil_overlay_img = Image.open(file)Then change if isTrue and self.overlay_img: self.photo.paste(self.overlay_img, (0, 0))to if isTrue and self.pil_overlay_img: self.photo.paste(self.pil_overlay_img, (0, 0)) RE: ImageTk Paste - deanhystad - Jun-01-2021 This is from the documentation for PIL ImageTk module. https://pillow.readthedocs.io/en/stable/reference/ImageTk.html Quote:paste(im, box=None)[source] Notice that im is a PIL image, not an ImageTk.PhotoImage. You need to load your overlay using Quote:PIL.Image.open(fp, mode='r', formats=None) RE: ImageTk Paste - KDog - Jun-02-2021 Lesson learned on imports ![]() Unfortunately neither of the above work. I don't get an error but the image saves without the overlay on it. RE: ImageTk Paste - Yoriz - Jun-02-2021 The error is gone, that's progress ![]() Try it with both images being pillow images Paste another image into an image with Python, Pillow |