Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ImageTk Paste
#11
Tried using PIL but still no joy:
import tkinter as tk
import ImageChops
import cv2
from PIL import Image, ImageTk, ImageEnhance
import PIL
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 = tk.Toplevel(self.window)
        self.newWindow.title("Settings")
        self.newWindow.geometry("400x400")
        self.btn_flip = tk.Button(self.newWindow, text="Mirror Image", width=10, command=self.flip_img)
        self.btn_flip.pack(side=tk.LEFT, padx=10)
            # self.brightness_lbl = Label(self.newWindow, text="Image Brightness")
            # self.brightness_lbl.pack(anchor=NW)
        #var = IntVar()
        self.brightness = tk.Scale(
        self.newWindow, length=200, from_=0, to=255, orient=tk.HORIZONTAL, label="Image Brightness",
                command=self.slide)
        self.brightness.set(200)
        self.brightness.pack(anchor=tk.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)
            self.pil_overlay_img = PIL.Image.open(file, mode='r', formats=None)

    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.pil_overlay_img:

            filename = filedialog.asksaveasfilename(
            defaultextension='.jpg', title="Choose Filename", filetypes=[("JPEG Files", '*.jpg')])
            filename = self.photo.paste(self.pil_overlay_img, (0,0))

    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=PIL.Image.fromarray(frame))
            #self.pil_photo_img = PIL.Image.open(self.photo, mode='r', formats=None)
            #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(10, 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()
Reply
#12
"no joy" is not very useful. Please describe the problem.
Reply
#13
The problem is when i go to save/snapshot an image it doesn't save the overlay on top of the video frame.
Also once i click save on the file dialog a new file dialog appears.

Here are my efforts at PIL:

This is in the update function where I attempt to convert the latest frame to PIL image
self.photo = ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
This is in the snapshot function where I attempt to paste the overlay onto the latest frame
filename = self.photo.paste(self.pil_overlay_img, (0,0))
Reply
#14
The snapshot method opens a dialog, and if successful opens a second dialog. It opens two dialogs because you wrote the code that way. Then it proceeds to perform actions that don't make sense. Your problem is not with the image stuff, it is with everything else that has become a real mess because you've been thrashing away on this problem. Time to step back and get a simple example working.

I gutted your code, removing the video capture, and pared it down to a program that opens image files and pastes them on an existing image. The pasted image is centered where the cursor was when the mouse button was pressed inside the canvas object. There is a save button to write the resulting image to a file.
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import filedialog

filetypes = (('PNG', '*.png'), ('JPEG', '*.jpg'), ('All', '*.*'))

class App:
    def __init__(self):
        self.image = None
        self.tk_image = None
        window = tk.Tk()
        self.canvas = tk.Canvas(window, width=360, height=360)
        self.canvas.pack()
        self.canvas.bind('<Button>', self.open)
        button = tk.Button(window, text="Save", command=self.save)
        button.pack(side=tk.LEFT, padx=10)
        window.mainloop()
 
    def open(self, event):
        """Open image file and paste to existing image"""
        filename = filedialog.askopenfile(mode='rb', filetypes=filetypes)
        if filename:
            # Open image file.  This creates a PIL image file object
            image = Image.open(filename)
            if self.image is None:
                # Set initial image
                self.image = image
            else:
                # Paste new image on existing image
                x = event.x - image.width//2
                y = event.y - image.height//2
                Image.Image.paste(self.image, image, (x, y))
            # Draw image on canvas.  Must convert to Tk image first
            self.tk_image = ImageTk.PhotoImage(self.image)
            self.canvas.delete(tk.ALL)
            self.canvas.create_image(180, 180,  image=self.tk_image)

    def save(self):
        """Save image to a file"""
        filename = filedialog.asksaveasfilename(filetypes=filetypes)
        if filename:
            self.image.save(filename)
  
if __name__ == "__main__":
    App()
I have never used PIL this way and I was surprised to see that paste is a function and not a method. You pass the base and pasted images as arguments. There may be a paste method for ImageTk objects, I haven't looked, but you should. You can use my short example to experiment around with that.

When you are trying something new you should write a short example instead of messing up existing code. The test program should be only as long as it has to be to test the idea. A nice side benefit of this type of development is you can keep all these programs in a folder, like a design notebook, and reference them later when you have questions about how to use different Python libraries. The short examples are also great for posting to the forum.
Reply
#15
Many thanks for this deanhystad! I will play around with it.
Your second suggestion is a great idea, it will simplify things and leave me with a handy library of mini apps.

Big Grin
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  What script to paste folders thenewcoder 1 664 Nov-29-2023, 09:40 AM
Last Post: Pedroski55
  PIL ImageTk issue with MATPLOTLIB garynewport 0 1,764 Jan-17-2023, 11:32 AM
Last Post: garynewport
  Please help me [copy and paste file from src to dst] midomarc 2 1,009 Nov-24-2022, 10:13 PM
Last Post: midomarc
  Cut and Paste Oshadha 3 2,433 Jan-20-2021, 04:27 PM
Last Post: spaceraiders
  copy paste file and re-name it asheru93 1 2,370 May-24-2019, 10:43 AM
Last Post: heiner55

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020