我有一個簡單的 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()
問題是當我嘗試使用函式 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顯示視窗。
這是更正的代碼:
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 視窗中做到這一點,我將不勝感激。
我將不勝感激任何幫助。
邁克爾
uj5u.com熱心網友回復:
在 show_frame() 中,您使用 opencv 作為 Mat 讀取幀,然后使用 self.imgtk = ImageTk.PhotoImage(im11) 將其轉換為 PIL 影像。然后當你呼叫 set_default_points() 來使用 cv2.circle(self.imgtk, ... ) 時,你又需要 Mat 了。
編輯:您需要使用 opencv 格式(Mat)進行 opencv 操作,然后當您想要顯示結果時,Tkinter 需要 PIL 格式。
opencv_image --> opencv_operation --> ImageTk.PhotoImage() --> Tkinter 顯示
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/471343.html
標籤:Python 图片 opencv tkinter 编辑
