Я создаю предварительный просмотр изображения для моей программы обработки изображений.Поэтому я использую tkinter canvas , чтобы отобразить, увеличить и сместить изображение.Чтобы сохранить память, я хочу обрезать и изменить размер изображения, используя подушку , чтобы отображалась только видимая часть.
Вот так
Красная вырезанная часть должна быть вырезана
Для смещения изображения я использовал технику из этого вопроса и для обрезки изображения я пытался изменить код из этого вопроса
Таким образом, я закончил с этим кодом (пытался максимально упростить его)
Часть обрезки находится в функции redraw :
import tkinter as tk
import tkinter.ttk as ttk
from PIL import Image, ImageTk
class ImagePreview(ttk.Frame):
def __init__(self, master=None):
ttk.Frame.__init__(self, master)
self.master.geometry('800x500+10+10')
self.master.columnconfigure(0, weight=1)
self.master.rowconfigure(0, weight=1)
self.grid(column=0, row=0, sticky=tk.N+tk.E+tk.S+tk.W)
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.master.update()
self.canvasScale = 1.0 # Multiplier to scale the image
self.sourceImage = Image.open('./test.jpg') # Must be taller than wide, for now
self.previewImage = ImageTk.PhotoImage(self.sourceImage) # Cropped Image
self.previewImage_ID = None # Canvas item ID of the cropped Image
self.init_widgets()
self.master.update_idletasks()
self.set_binds()
self.adjust_canvas_size()
def init_widgets(self):
self.previewCanvas = tk.Canvas(self)
self.previewCanvas.config(background='yellow', highlightthickness=0, width=self.winfo_width(), height=self.winfo_height())
self.previewCanvas.grid(column=0, row=0)
def set_binds(self):
self.bind('<Configure>', self.adjust_canvas_size) # Makes the canvas always the same size as the main frame
self.previewCanvas.bind("<ButtonPress-1>", self.shift_start)
self.previewCanvas.bind("<B1-Motion>", self.shift_move)
self.previewCanvas.bind('<MouseWheel>', self.zoom)
def adjust_canvas_size(self, event=None):
self.previewCanvas.config(width=self.winfo_width()-5, height=self.winfo_height()-5)
self.canvasScale = self.previewCanvas.winfo_height() / self.sourceImage.height
self.redraw()
def shift_start(self, event):
self.previewCanvas.scan_mark(event.x, event.y)
def shift_move(self, event):
self.previewCanvas.scan_dragto(event.x, event.y, gain=1)
self.redraw()
def zoom(self, event):
if event.delta >= 0 and self.canvasScale < 5:
self.canvasScale *= 1.2
if event.delta < 0 and self.canvasScale > 0.1:
self.canvasScale *= 1/1.2
self.redraw()
def redraw(self):
if self.previewImage_ID:
self.previewCanvas.delete(self.previewImage_ID)
self.previewCanvas.delete(self.orientationLine)
imageWidth, imageHeight = self.sourceImage.size
# Crop size to make image and canvas scale 1:1
cropWidth, cropHeight = int(imageWidth*self.canvasScale), int(imageHeight*self.canvasScale)
size = cropWidth, cropHeight
tempImage = self.sourceImage.resize(size, resample=Image.LANCZOS)
if self.sourceImage.height > self.sourceImage.width: # Didn't came up with an idea yet to crop wide and tall pictures with the same algorithm
# The distance between left (or right) edge of the canvas
# and the left (or right) edge of the original image position
z = int((self.previewCanvas.winfo_width()-cropWidth)/2)+1
# To simplify it, I'm just cutting the left edge,
# when the image hits the left edge of the canvas
if self.previewCanvas.canvasx(0) > z:
leftEdge = self.previewCanvas.canvasx(0) - z
print('Cut:',leftEdge)
else:
leftEdge = 0
rightEdge = tempImage.width
topEdge = 0
bottomEdge = tempImage.height
#crop
tempImage = tempImage.crop((leftEdge, topEdge, rightEdge, bottomEdge))
# To check that there is no overhang, on the not visible part of the canvas,
# what is sadly the problem
tempImage.save('./croppedImage.jpg')
# draw
x = self.previewCanvas.winfo_width() / 2
y = self.previewCanvas.winfo_height() / 2
self.previewImage = ImageTk.PhotoImage(tempImage)
self.previewImage_ID = self.previewCanvas.create_image(x, y, image=self.previewImage)
self.previewCanvas.scale(tk.ALL, x, y,self.canvasScale, self.canvasScale)
self.orientationLine = self.previewCanvas.create_line(0,0,self.previewCanvas.winfo_width(),self.previewCanvas.winfo_height())
def main():
root = tk.Tk()
application = ImagePreview(root)
application.mainloop()
if __name__ == '__main__':
main()
Проблема в том, что изображение теоретически обрезается правильно (разница между измененным размером и перекрывающейся, невидимой частью правильная).Но если я сохраню обрезанное изображение, я ясно вижу, что за границей все еще есть часть, которая не обрезается.Хуже всего то, что это перекрытие увеличивается, чем больше изображение перемещается за границы.
Я почти уверен, что что-то упустил при масштабировании, но я не смог найти ошибку впрошедшие дни.