使用TKINTER显示和编辑OpenCV映像

发布于 2025-01-26 10:54:25 字数 16286 浏览 4 评论 0原文

我有一个简单的GUI应用程序,可以显示从选定的相机和转换图像中显示图像。要进行转换,我需要在图像上选择4分,因此我想通过单击来挑选它们。但是,为了做到这一点,图片必须是openCV图像。因此,我的想法是将图像从相机中“冻结”,然后对其进行编辑(例如,通过函数set_default_points()绘制4个圆圈)。

这是我的代码

(现在的密钥函数是show_frame(),freeze_camera()和set_default_points())

from tkinter import *
from tkinter import ttk
import cv2
import numpy as np
from PIL import Image, ImageTk

# ----- SET -----
GREY = "#D8D8D8"
BLUE = "#81F7F3"
AQUA = "#9CC3D5"
ICE = "#C7D3D4"

canvas_width = 500
canvas_height = 600


def camera_amount():
    '''Returns int value of available camera devices connected to the host device
    from url: https://www.codegrepper.com/code-examples/python/how+to+count+how+many+cameras+you+have+with+python
    '''
    camera = 0
    while True:
        if (cv2.VideoCapture(camera).grab()) is True:
            camera = camera + 1
        else:
            cv2.destroyAllWindows()
            return camera



# ----- default start images -----
img1 = cv2.imread("d1.png")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
resized1 = cv2.resize(img1, (canvas_width, canvas_height))

img2 = cv2.imread("ipm.png")
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
resized2 = cv2.resize(img2, (canvas_width, canvas_height))


# ----- Application ------
class App(Tk):
    def __init__(self):
        super().__init__()

        self.title("Image TOP-DOWN Tranformation")
        self.minsize(width=1200, height=700)
        self.config(padx=5, pady=5, bg=ICE)

        im1 = Image.fromarray(resized1)
        im2 = Image.fromarray(resized2)
        self.org_img = ImageTk.PhotoImage(im1)
        self.transf_img = ImageTk.PhotoImage(im2)

        self.canvas1 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.org_image_container = self.canvas1.create_image(250, 300, anchor="center", image=self.org_img)
        self.canvas1.grid(padx=10, pady=10, row=1, rowspan=8, column=0)

        self.canvas2 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.trf_image_container = self.canvas2.create_image(250, 300, anchor="center", image=self.transf_img)
        self.canvas2.grid(padx=10, pady=10, row=1, rowspan=8, column=1)

        # ----- Labels -----
        self.label1 = Label(text="Original image")
        self.label1.grid(row=0, column=0)

        self.label2 = Label(text="Transformed image")
        self.label2.grid(row=0, column=1)

        self.label3 = Label(text="Functionalities")
        self.label3.grid(row=0, column=2)

        self.move_label = Label(text="Move points")
        self.move_label.grid(row=8, column=2, columnspan=3)

        # ----- Buttons -----

        self.selected_camera = StringVar()
        self.camera_cb = ttk.Combobox(self, textvariable=self.selected_camera)
        self.camera_cb.grid(padx=5, pady=5, row=1, column=2, columnspan=3)
        amount_of_cameras = camera_amount()
        self.camera_cb['values'] = [i for i in range(amount_of_cameras)]
        self.camera_cb.current(0)

        self.change_camera = Button(text="Change Camera", command=self.choose_camera)
        self.change_camera.grid(padx=5, pady=5, row=2, column=2, columnspan=3)

        self.freeze_cam = Button(text="Freeze camera", command=self.freeze_camera)
        self.freeze_cam.grid(padx=5, pady=5, row=3, column=2, columnspan=3)

        self.default_points = Button(text="Set default points", command=self.set_default_points)
        self.default_points.grid(padx=5, pady=5, row=4, column=2, columnspan=3)

        self.transform_button = Button(text="Transform", command=self.transform)
        self.transform_button.grid(padx=5, pady=5, row=5, column=2, columnspan=3)

        self.clear_button = Button(text="CLEAR", command=self.clear)
        self.clear_button.grid(padx=5, pady=5, row=6, column=2, columnspan=3)

        self.save_button = Button(text="SAVE", command=self.save)
        self.save_button.grid(padx=5, pady=5, row=7, column=2, columnspan=3)

        self.up_butt = Button(text="↑", command=self.up)
        self.up_butt.grid(padx=5, pady=5, row=9, column=3)

        self.down_butt = Button(text="↓", command=self.down)
        self.down_butt.grid(padx=5, pady=5, row=10, column=3)

        self.left_butt = Button(text="←", command=self.left)
        self.left_butt.grid(padx=5, pady=5, row=10, column=2, columnspan=2)

        self.right_butt = Button(text="→", command=self.right)
        self.right_butt.grid(padx=5, pady=5, row=10, column=4)

        # ----- fields -----
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.imgtk = None
        self.after_id = None
        self.resized11 = None

        self.input_points = []
        self.output_points = []

    def choose_camera(self):
        self.freeze_camera()
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.show_frame()

    # po upakowaniu w klasę po prostu zmienić jedno pole, a w funkcji odpowiedzialnej za wyświetlanie kamery dac ifa
    def freeze_camera(self):
        self.canvas1.after_cancel(self.after_id)
        # now, the image on canvas is freezed,
        # it means, that the picture is last imgtk from show_frame function,
        # but it's not like cv2 image, and it cannot be editable
        # I tried example like this, but it doesn't work
        # pil_image = PIL.Image.open('Image.jpg').convert('RGB')
        # open_cv_image = numpy.array(pil_image)
        # # Convert RGB to BGR
        # open_cv_image = open_cv_image[:, :, ::-1].copy()

    def set_default_points(self):
        default_points = [[448, 609], [580,609], [580,741], [448,741]]
        for pts in default_points:
            cv2.circle(self.imgtk, pts, 5, (0, 0, 255), -2)
        pass

    def transform(self):
        pass

    def clear(self):
        pass

    def save(self):
        pass

    def up(self):
        pass

    def down(self):
        pass

    def left(self):
        pass

    def right(self):
        pass

    def draw_circle(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDBLCLK:
            cv2.circle(self.imgtk, (x, y), 5, (255, 0, 0), -2)
            self.input_points.append([x, y])
        if event == cv2.EVENT_RBUTTONDBLCLK:
            cv2.circle(self.imgtk, (x, y), 5, (0, 0, 255), -2)
            self.output_points.append([x, y])

    def show_frame(self):
        """
        https://www.tutorialspoint.com/how-to-show-webcam-in-tkinter-window
        """
        img11 = self.cap.read()[1]
        cv2image = cv2.cvtColor(img11, cv2.COLOR_BGR2RGB)
        self.resized11 = cv2.resize(cv2image, (canvas_width, canvas_height))
        im11 = Image.fromarray(self.resized11)
        self.imgtk = ImageTk.PhotoImage(im11)
        self.canvas1.imgtk = self.imgtk
        self.canvas1.itemconfig(self.org_image_container, image=self.imgtk)
        self.after_id = self.canvas1.after(10, self.show_frame)




if __name__ == "__main__":
    app = App()
    app.show_frame()
    app.mainloop()

问题是当我尝试通过使用function set_default_points我遇到此错误:编辑:

Exception in Tkinter callback
Traceback (most recent call last):
  File "D:\Python_versions\lib\tkinter\__init__.py", line 1921, in __call__
    return self.func(*args)
  File "D:\DUCKIETOWN\aplikacja_do_transformacji_obrazu\APP.py", line 146, in set_default_points
    cv2.circle(self.imgtk, pts, 5, (0, 0, 255), -2)
cv2.error: OpenCV(4.5.5) :-1: error: (-5:Bad argument) in function 'circle'
> Overload resolution failed:
>  - img is not a numpy array, neither a scalar
>  - Expected Ptr<cv::UMat> for argument 'img'

看起来:因此,似乎是这样,似乎就像实现我的目标的唯一方法是进行操作,例如使用OpENCV映像在独立的OpenCV窗口上选择转换点,然后将其上传到我的TKINTER DISPLAIND窗口。

这是校正的代码:

import tkinter.messagebox
from tkinter import *
from tkinter import ttk
import cv2
import numpy as np
from PIL import Image, ImageTk
import transform


# ----- SET -----
GREY = "#D8D8D8"
BLUE = "#81F7F3"
AQUA = "#9CC3D5"
ICE = "#C7D3D4"

canvas_width = 500
canvas_height = 600


def camera_amount() -> int:
    '''Returns int value of available camera devices connected to the host device
    from url: https://www.codegrepper.com/code-examples/python/how+to+count+how+many+cameras+you+have+with+python
    '''
    camera = 0
    while True:
        if (cv2.VideoCapture(camera).grab()) is True:
            camera = camera + 1
        else:
            cv2.destroyAllWindows()
            return camera



# ----- default start images -----
img1 = cv2.imread("d1.png")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
resized1 = cv2.resize(img1, (canvas_width, canvas_height))

img2 = cv2.imread("ipm.png")
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
resized2 = cv2.resize(img2, (canvas_width, canvas_height))


# ----- Application ------
class App(Tk):
    def __init__(self):
        super().__init__()

        self.title("Image TOP-DOWN Tranformation")
        self.minsize(width=1200, height=700)
        self.config(padx=5, pady=5, bg=ICE)

        im1 = Image.fromarray(resized1)
        im2 = Image.fromarray(resized2)
        self.org_img = ImageTk.PhotoImage(im1)
        self.transf_img = ImageTk.PhotoImage(im2)

        self.canvas1 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.org_image_container = self.canvas1.create_image(250, 300, anchor="center", image=self.org_img)
        self.canvas1.grid(padx=10, pady=10, row=1, rowspan=8, column=0)

        self.canvas2 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.trf_image_container = self.canvas2.create_image(250, 300, anchor="center", image=self.transf_img)
        self.canvas2.grid(padx=10, pady=10, row=1, rowspan=8, column=1)

        # ----- Labels -----
        self.label1 = Label(text="Original image")
        self.label1.grid(row=0, column=0)

        self.label2 = Label(text="Transformed image")
        self.label2.grid(row=0, column=1)

        self.label3 = Label(text="Functionalities")
        self.label3.grid(row=0, column=2)

        self.move_label = Label(text="Move points")
        self.move_label.grid(row=8, column=2, columnspan=3)

        # ----- Buttons -----

        self.selected_camera = StringVar()
        self.camera_cb = ttk.Combobox(self, textvariable=self.selected_camera)
        self.camera_cb.grid(padx=5, pady=5, row=1, column=2, columnspan=3)
        amount_of_cameras = camera_amount()
        self.camera_cb['values'] = [i for i in range(amount_of_cameras)]
        self.camera_cb.current(0)

        self.change_camera = Button(text="Change Camera", command=self.choose_camera)
        self.change_camera.grid(padx=5, pady=5, row=2, column=2, columnspan=3)

        self.freeze_cam = Button(text="Freeze camera", command=self.freeze_camera)
        self.freeze_cam.grid(padx=5, pady=5, row=3, column=2, columnspan=3)

        self.default_points = Button(text="Set default points", command=self.set_default_points)
        self.default_points.grid(padx=5, pady=5, row=4, column=2, columnspan=3)

        self.transform_button = Button(text="Transform", command=self.transform)
        self.transform_button.grid(padx=5, pady=5, row=5, column=2, columnspan=3)

        self.clear_button = Button(text="CLEAR", command=self.clear)
        self.clear_button.grid(padx=5, pady=5, row=6, column=2, columnspan=3)

        self.save_button = Button(text="SAVE", command=self.save)
        self.save_button.grid(padx=5, pady=5, row=7, column=2, columnspan=3)

        self.up_butt = Button(text="↑", command=self.up)
        self.up_butt.grid(padx=5, pady=5, row=9, column=3)

        self.down_butt = Button(text="↓", command=self.down)
        self.down_butt.grid(padx=5, pady=5, row=10, column=3)

        self.left_butt = Button(text="←", command=self.left)
        self.left_butt.grid(padx=5, pady=5, row=10, column=2, columnspan=2)

        self.right_butt = Button(text="→", command=self.right)
        self.right_butt.grid(padx=5, pady=5, row=10, column=4)

        # ----- fields -----
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.imgtk = None
        self.after_id = None
        self.resized11 = None
        self.ipm_matrix = None

        self.input_points = []
        self.output_points = []

        self.ipm_matrixes = [] # list of saved ipm_matrixes which satisfied us

    def choose_camera(self) -> None:
        self.freeze_camera(display=False)
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.clear()

    def freeze_camera(self, display: bool = True) -> None:
        self.canvas1.after_cancel(self.after_id)
        if display:
            self.resized11 = cv2.cvtColor(self.resized11, cv2.COLOR_BGR2RGB)
            cv2.namedWindow("image")
            cv2.setMouseCallback("image", self.draw_circle)
            while True:
                cv2.imshow("image", self.resized11)
                if cv2.waitKey(1) & 0xFF == ord("q"):
                    break
            cv2.destroyAllWindows()

    def set_default_points(self) -> None:
        default_points = [[448, 609], [580,609], [580,741], [448,741]]
        for pts in default_points:
            cv2.circle(self.resized11, pts, 5, (0, 0, 255), -2)
        self.canvas1.itemconfig(self.org_image_container, image=self.resized11)

    def transform(self) -> None:
        if len(self.input_points) == 4 and len(self.output_points) == 4:
            ordered_pts = transform.order_points(np.array(self.input_points, dtype=np.float32))
            ordered_out_pts = transform.order_points(np.array(self.output_points, dtype=np.float32))

            self.ipm_matrix = cv2.getPerspectiveTransform(ordered_pts, ordered_out_pts)
            self.transf_img = cv2.warpPerspective(self.resized11, self.ipm_matrix, self.resized11.shape[:2][::-1])

            self.transf_img = cv2.cvtColor(self.transf_img, cv2.COLOR_BGR2RGB)
            self.transf_img = Image.fromarray(self.transf_img)
            warpedtk = ImageTk.PhotoImage(self.transf_img)
            self.canvas2.warpedtk = warpedtk
            self.canvas2.itemconfig(self.trf_image_container, image=warpedtk)
        else:
            tkinter.messagebox.showwarning("Warning", "Choose right points to transformation!")

    def clear(self) -> None:
        self.output_points = []
        self.input_points = []
        self.show_frame()

    def save(self) -> None:
        # add to self.ipm_matrixes tuple (camera number, ipm_matrix)
        self.ipm_matrixes.append((int(self.selected_camera.get()), self.ipm_matrix))
        answer = tkinter.messagebox.askyesno("Save", "Do you want to save your all ipm matrixes to file?")
        if answer:
            self.safe_to_file()
        pass

    def up(self) -> None:
        pass

    def down(self) -> None:
        pass

    def left(self) -> None:
        pass

    def right(self) -> None:
        pass

    def draw_circle(self, event, x, y, flags, param) -> None:
        if event == cv2.EVENT_LBUTTONDBLCLK:
            cv2.circle(self.resized11, (x, y), 5, (255, 0, 0), -2)
            self.input_points.append([x, y])
        if event == cv2.EVENT_RBUTTONDBLCLK:
            cv2.circle(self.resized11, (x, y), 5, (0, 0, 255), -2)
            self.output_points.append([x, y])

    def show_frame(self) -> None:
        """
        https://www.tutorialspoint.com/how-to-show-webcam-in-tkinter-window
        """
        img11 = self.cap.read()[1]
        cv2image = cv2.cvtColor(img11, cv2.COLOR_BGR2RGB)
        self.resized11 = cv2.resize(cv2image, (canvas_width, canvas_height))
        im11 = Image.fromarray(self.resized11)
        self.imgtk = ImageTk.PhotoImage(im11)
        self.canvas1.imgtk = self.imgtk
        self.canvas1.itemconfig(self.org_image_container, image=self.imgtk)
        self.after_id = self.canvas1.after(10, self.show_frame)

    def safe_to_file(self) -> None:
        with open("saved_conf.txt", "a") as file:
            res = ""
            for elem in self.ipm_matrixes:
                res += f"Camera {elem[0]}: {elem[1]}\n"
            res += "\n\n"
            file.write(res)





if __name__ == "__main__":
    app = App()
    app.show_frame()
    app.mainloop()

但是,如果有人有不同的想法,我该如何在一个单一的tkinter窗口中做到这一点,我会很感激。

感谢任何帮助。

迈克尔

I have a simple GUI app to display image from chosen camera and transformed image. To do transformation I need to choose 4 points on image, so I would like to pick them by clicking. But in order to do that the picture must be an opencv image. So my idea was to "freeze" the image from camera and then edit it (for example by drawing on it 4 circles by function set_default_points()).

Here is my code

(The key function for now are show_frame(), freeze_camera() and set_default_points())

from tkinter import *
from tkinter import ttk
import cv2
import numpy as np
from PIL import Image, ImageTk

# ----- SET -----
GREY = "#D8D8D8"
BLUE = "#81F7F3"
AQUA = "#9CC3D5"
ICE = "#C7D3D4"

canvas_width = 500
canvas_height = 600


def camera_amount():
    '''Returns int value of available camera devices connected to the host device
    from url: https://www.codegrepper.com/code-examples/python/how+to+count+how+many+cameras+you+have+with+python
    '''
    camera = 0
    while True:
        if (cv2.VideoCapture(camera).grab()) is True:
            camera = camera + 1
        else:
            cv2.destroyAllWindows()
            return camera



# ----- default start images -----
img1 = cv2.imread("d1.png")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
resized1 = cv2.resize(img1, (canvas_width, canvas_height))

img2 = cv2.imread("ipm.png")
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
resized2 = cv2.resize(img2, (canvas_width, canvas_height))


# ----- Application ------
class App(Tk):
    def __init__(self):
        super().__init__()

        self.title("Image TOP-DOWN Tranformation")
        self.minsize(width=1200, height=700)
        self.config(padx=5, pady=5, bg=ICE)

        im1 = Image.fromarray(resized1)
        im2 = Image.fromarray(resized2)
        self.org_img = ImageTk.PhotoImage(im1)
        self.transf_img = ImageTk.PhotoImage(im2)

        self.canvas1 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.org_image_container = self.canvas1.create_image(250, 300, anchor="center", image=self.org_img)
        self.canvas1.grid(padx=10, pady=10, row=1, rowspan=8, column=0)

        self.canvas2 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.trf_image_container = self.canvas2.create_image(250, 300, anchor="center", image=self.transf_img)
        self.canvas2.grid(padx=10, pady=10, row=1, rowspan=8, column=1)

        # ----- Labels -----
        self.label1 = Label(text="Original image")
        self.label1.grid(row=0, column=0)

        self.label2 = Label(text="Transformed image")
        self.label2.grid(row=0, column=1)

        self.label3 = Label(text="Functionalities")
        self.label3.grid(row=0, column=2)

        self.move_label = Label(text="Move points")
        self.move_label.grid(row=8, column=2, columnspan=3)

        # ----- Buttons -----

        self.selected_camera = StringVar()
        self.camera_cb = ttk.Combobox(self, textvariable=self.selected_camera)
        self.camera_cb.grid(padx=5, pady=5, row=1, column=2, columnspan=3)
        amount_of_cameras = camera_amount()
        self.camera_cb['values'] = [i for i in range(amount_of_cameras)]
        self.camera_cb.current(0)

        self.change_camera = Button(text="Change Camera", command=self.choose_camera)
        self.change_camera.grid(padx=5, pady=5, row=2, column=2, columnspan=3)

        self.freeze_cam = Button(text="Freeze camera", command=self.freeze_camera)
        self.freeze_cam.grid(padx=5, pady=5, row=3, column=2, columnspan=3)

        self.default_points = Button(text="Set default points", command=self.set_default_points)
        self.default_points.grid(padx=5, pady=5, row=4, column=2, columnspan=3)

        self.transform_button = Button(text="Transform", command=self.transform)
        self.transform_button.grid(padx=5, pady=5, row=5, column=2, columnspan=3)

        self.clear_button = Button(text="CLEAR", command=self.clear)
        self.clear_button.grid(padx=5, pady=5, row=6, column=2, columnspan=3)

        self.save_button = Button(text="SAVE", command=self.save)
        self.save_button.grid(padx=5, pady=5, row=7, column=2, columnspan=3)

        self.up_butt = Button(text="↑", command=self.up)
        self.up_butt.grid(padx=5, pady=5, row=9, column=3)

        self.down_butt = Button(text="↓", command=self.down)
        self.down_butt.grid(padx=5, pady=5, row=10, column=3)

        self.left_butt = Button(text="←", command=self.left)
        self.left_butt.grid(padx=5, pady=5, row=10, column=2, columnspan=2)

        self.right_butt = Button(text="→", command=self.right)
        self.right_butt.grid(padx=5, pady=5, row=10, column=4)

        # ----- fields -----
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.imgtk = None
        self.after_id = None
        self.resized11 = None

        self.input_points = []
        self.output_points = []

    def choose_camera(self):
        self.freeze_camera()
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.show_frame()

    # po upakowaniu w klasę po prostu zmienić jedno pole, a w funkcji odpowiedzialnej za wyświetlanie kamery dac ifa
    def freeze_camera(self):
        self.canvas1.after_cancel(self.after_id)
        # now, the image on canvas is freezed,
        # it means, that the picture is last imgtk from show_frame function,
        # but it's not like cv2 image, and it cannot be editable
        # I tried example like this, but it doesn't work
        # pil_image = PIL.Image.open('Image.jpg').convert('RGB')
        # open_cv_image = numpy.array(pil_image)
        # # Convert RGB to BGR
        # open_cv_image = open_cv_image[:, :, ::-1].copy()

    def set_default_points(self):
        default_points = [[448, 609], [580,609], [580,741], [448,741]]
        for pts in default_points:
            cv2.circle(self.imgtk, pts, 5, (0, 0, 255), -2)
        pass

    def transform(self):
        pass

    def clear(self):
        pass

    def save(self):
        pass

    def up(self):
        pass

    def down(self):
        pass

    def left(self):
        pass

    def right(self):
        pass

    def draw_circle(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDBLCLK:
            cv2.circle(self.imgtk, (x, y), 5, (255, 0, 0), -2)
            self.input_points.append([x, y])
        if event == cv2.EVENT_RBUTTONDBLCLK:
            cv2.circle(self.imgtk, (x, y), 5, (0, 0, 255), -2)
            self.output_points.append([x, y])

    def show_frame(self):
        """
        https://www.tutorialspoint.com/how-to-show-webcam-in-tkinter-window
        """
        img11 = self.cap.read()[1]
        cv2image = cv2.cvtColor(img11, cv2.COLOR_BGR2RGB)
        self.resized11 = cv2.resize(cv2image, (canvas_width, canvas_height))
        im11 = Image.fromarray(self.resized11)
        self.imgtk = ImageTk.PhotoImage(im11)
        self.canvas1.imgtk = self.imgtk
        self.canvas1.itemconfig(self.org_image_container, image=self.imgtk)
        self.after_id = self.canvas1.after(10, self.show_frame)




if __name__ == "__main__":
    app = App()
    app.show_frame()
    app.mainloop()

The problem is when I try to edit that frozen picture, by using function set_default_points I occure this error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "D:\Python_versions\lib\tkinter\__init__.py", line 1921, in __call__
    return self.func(*args)
  File "D:\DUCKIETOWN\aplikacja_do_transformacji_obrazu\APP.py", line 146, in set_default_points
    cv2.circle(self.imgtk, pts, 5, (0, 0, 255), -2)
cv2.error: OpenCV(4.5.5) :-1: error: (-5:Bad argument) in function 'circle'
> Overload resolution failed:
>  - img is not a numpy array, neither a scalar
>  - Expected Ptr<cv::UMat> for argument 'img'

EDIT: So it seems, like only way to achieve my goal is to do operations like choose transformation points on detached opencv window with opencv image, and then upload it to my tkinter display window.

Here is corrected code:

import tkinter.messagebox
from tkinter import *
from tkinter import ttk
import cv2
import numpy as np
from PIL import Image, ImageTk
import transform


# ----- SET -----
GREY = "#D8D8D8"
BLUE = "#81F7F3"
AQUA = "#9CC3D5"
ICE = "#C7D3D4"

canvas_width = 500
canvas_height = 600


def camera_amount() -> int:
    '''Returns int value of available camera devices connected to the host device
    from url: https://www.codegrepper.com/code-examples/python/how+to+count+how+many+cameras+you+have+with+python
    '''
    camera = 0
    while True:
        if (cv2.VideoCapture(camera).grab()) is True:
            camera = camera + 1
        else:
            cv2.destroyAllWindows()
            return camera



# ----- default start images -----
img1 = cv2.imread("d1.png")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
resized1 = cv2.resize(img1, (canvas_width, canvas_height))

img2 = cv2.imread("ipm.png")
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
resized2 = cv2.resize(img2, (canvas_width, canvas_height))


# ----- Application ------
class App(Tk):
    def __init__(self):
        super().__init__()

        self.title("Image TOP-DOWN Tranformation")
        self.minsize(width=1200, height=700)
        self.config(padx=5, pady=5, bg=ICE)

        im1 = Image.fromarray(resized1)
        im2 = Image.fromarray(resized2)
        self.org_img = ImageTk.PhotoImage(im1)
        self.transf_img = ImageTk.PhotoImage(im2)

        self.canvas1 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.org_image_container = self.canvas1.create_image(250, 300, anchor="center", image=self.org_img)
        self.canvas1.grid(padx=10, pady=10, row=1, rowspan=8, column=0)

        self.canvas2 = Canvas(width=500, height=600, bg=ICE, highlightthickness=2)
        self.trf_image_container = self.canvas2.create_image(250, 300, anchor="center", image=self.transf_img)
        self.canvas2.grid(padx=10, pady=10, row=1, rowspan=8, column=1)

        # ----- Labels -----
        self.label1 = Label(text="Original image")
        self.label1.grid(row=0, column=0)

        self.label2 = Label(text="Transformed image")
        self.label2.grid(row=0, column=1)

        self.label3 = Label(text="Functionalities")
        self.label3.grid(row=0, column=2)

        self.move_label = Label(text="Move points")
        self.move_label.grid(row=8, column=2, columnspan=3)

        # ----- Buttons -----

        self.selected_camera = StringVar()
        self.camera_cb = ttk.Combobox(self, textvariable=self.selected_camera)
        self.camera_cb.grid(padx=5, pady=5, row=1, column=2, columnspan=3)
        amount_of_cameras = camera_amount()
        self.camera_cb['values'] = [i for i in range(amount_of_cameras)]
        self.camera_cb.current(0)

        self.change_camera = Button(text="Change Camera", command=self.choose_camera)
        self.change_camera.grid(padx=5, pady=5, row=2, column=2, columnspan=3)

        self.freeze_cam = Button(text="Freeze camera", command=self.freeze_camera)
        self.freeze_cam.grid(padx=5, pady=5, row=3, column=2, columnspan=3)

        self.default_points = Button(text="Set default points", command=self.set_default_points)
        self.default_points.grid(padx=5, pady=5, row=4, column=2, columnspan=3)

        self.transform_button = Button(text="Transform", command=self.transform)
        self.transform_button.grid(padx=5, pady=5, row=5, column=2, columnspan=3)

        self.clear_button = Button(text="CLEAR", command=self.clear)
        self.clear_button.grid(padx=5, pady=5, row=6, column=2, columnspan=3)

        self.save_button = Button(text="SAVE", command=self.save)
        self.save_button.grid(padx=5, pady=5, row=7, column=2, columnspan=3)

        self.up_butt = Button(text="↑", command=self.up)
        self.up_butt.grid(padx=5, pady=5, row=9, column=3)

        self.down_butt = Button(text="↓", command=self.down)
        self.down_butt.grid(padx=5, pady=5, row=10, column=3)

        self.left_butt = Button(text="←", command=self.left)
        self.left_butt.grid(padx=5, pady=5, row=10, column=2, columnspan=2)

        self.right_butt = Button(text="→", command=self.right)
        self.right_butt.grid(padx=5, pady=5, row=10, column=4)

        # ----- fields -----
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.imgtk = None
        self.after_id = None
        self.resized11 = None
        self.ipm_matrix = None

        self.input_points = []
        self.output_points = []

        self.ipm_matrixes = [] # list of saved ipm_matrixes which satisfied us

    def choose_camera(self) -> None:
        self.freeze_camera(display=False)
        self.cap = cv2.VideoCapture(int(self.selected_camera.get()))
        self.clear()

    def freeze_camera(self, display: bool = True) -> None:
        self.canvas1.after_cancel(self.after_id)
        if display:
            self.resized11 = cv2.cvtColor(self.resized11, cv2.COLOR_BGR2RGB)
            cv2.namedWindow("image")
            cv2.setMouseCallback("image", self.draw_circle)
            while True:
                cv2.imshow("image", self.resized11)
                if cv2.waitKey(1) & 0xFF == ord("q"):
                    break
            cv2.destroyAllWindows()

    def set_default_points(self) -> None:
        default_points = [[448, 609], [580,609], [580,741], [448,741]]
        for pts in default_points:
            cv2.circle(self.resized11, pts, 5, (0, 0, 255), -2)
        self.canvas1.itemconfig(self.org_image_container, image=self.resized11)

    def transform(self) -> None:
        if len(self.input_points) == 4 and len(self.output_points) == 4:
            ordered_pts = transform.order_points(np.array(self.input_points, dtype=np.float32))
            ordered_out_pts = transform.order_points(np.array(self.output_points, dtype=np.float32))

            self.ipm_matrix = cv2.getPerspectiveTransform(ordered_pts, ordered_out_pts)
            self.transf_img = cv2.warpPerspective(self.resized11, self.ipm_matrix, self.resized11.shape[:2][::-1])

            self.transf_img = cv2.cvtColor(self.transf_img, cv2.COLOR_BGR2RGB)
            self.transf_img = Image.fromarray(self.transf_img)
            warpedtk = ImageTk.PhotoImage(self.transf_img)
            self.canvas2.warpedtk = warpedtk
            self.canvas2.itemconfig(self.trf_image_container, image=warpedtk)
        else:
            tkinter.messagebox.showwarning("Warning", "Choose right points to transformation!")

    def clear(self) -> None:
        self.output_points = []
        self.input_points = []
        self.show_frame()

    def save(self) -> None:
        # add to self.ipm_matrixes tuple (camera number, ipm_matrix)
        self.ipm_matrixes.append((int(self.selected_camera.get()), self.ipm_matrix))
        answer = tkinter.messagebox.askyesno("Save", "Do you want to save your all ipm matrixes to file?")
        if answer:
            self.safe_to_file()
        pass

    def up(self) -> None:
        pass

    def down(self) -> None:
        pass

    def left(self) -> None:
        pass

    def right(self) -> None:
        pass

    def draw_circle(self, event, x, y, flags, param) -> None:
        if event == cv2.EVENT_LBUTTONDBLCLK:
            cv2.circle(self.resized11, (x, y), 5, (255, 0, 0), -2)
            self.input_points.append([x, y])
        if event == cv2.EVENT_RBUTTONDBLCLK:
            cv2.circle(self.resized11, (x, y), 5, (0, 0, 255), -2)
            self.output_points.append([x, y])

    def show_frame(self) -> None:
        """
        https://www.tutorialspoint.com/how-to-show-webcam-in-tkinter-window
        """
        img11 = self.cap.read()[1]
        cv2image = cv2.cvtColor(img11, cv2.COLOR_BGR2RGB)
        self.resized11 = cv2.resize(cv2image, (canvas_width, canvas_height))
        im11 = Image.fromarray(self.resized11)
        self.imgtk = ImageTk.PhotoImage(im11)
        self.canvas1.imgtk = self.imgtk
        self.canvas1.itemconfig(self.org_image_container, image=self.imgtk)
        self.after_id = self.canvas1.after(10, self.show_frame)

    def safe_to_file(self) -> None:
        with open("saved_conf.txt", "a") as file:
            res = ""
            for elem in self.ipm_matrixes:
                res += f"Camera {elem[0]}: {elem[1]}\n"
            res += "\n\n"
            file.write(res)





if __name__ == "__main__":
    app = App()
    app.show_frame()
    app.mainloop()

But if anybody had different idea, how could I do that in one, single tkinter window I would be grateful.

I would appreciate any help.

Michael

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

恬淡成诗 2025-02-02 10:54:25

在show_frame()中,您使用openCV读取框架,然后用self.imgtk = imagetk.photoimage(im11)将其掩盖到pil图像中。
然后,当您调用set_default_points()使用cv2.circle(self.imgtk,...)时,您需要再次使用垫子。

编辑:
您需要使用OPENCV格式(MAT)进行操作操作,然后在要显示结果时,TKINTER需要PIL格式。

opencv_image-&gt; opencv_operation-&gt; imagetk.photoimage() - &gt; tkinter显示

In show_frame() you read frame with opencv as Mat then you covert it into PIL image with self.imgtk = ImageTk.PhotoImage(im11).
And then when you call set_default_points() to use cv2.circle(self.imgtk, ... ), you need Mat again.

EDIT:
You need to do opencv operation with opencv format (Mat) and then when you want to display result, Tkinter needs PIL format.

opencv_image --> opencv_operation --> ImageTk.PhotoImage() --> Tkinter display

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文