Обрезать видимую часть изображения на сдвинутом холсте tkinter - PullRequest
0 голосов
/ 23 мая 2019

Я создаю предварительный просмотр изображения для моей программы обработки изображений.Поэтому я использую 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()

Проблема в том, что изображение теоретически обрезается правильно (разница между измененным размером и перекрывающейся, невидимой частью правильная).Но если я сохраню обрезанное изображение, я ясно вижу, что за границей все еще есть часть, которая не обрезается.Хуже всего то, что это перекрытие увеличивается, чем больше изображение перемещается за границы.

Я почти уверен, что что-то упустил при масштабировании, но я не смог найти ошибку впрошедшие дни.

...