полностью переделай этот код - он неверный. задача:
1. считать фоновое изображение с лицом, целевое изображение с лицом
2. наложить целевое изображение на фоновое, при этом центры носов должны совпадать
3. пользователь может масштабировать целевое изображение (делать его больше/меньше), при этом всегда центры носов должны совпадать
4. пользователь может перемещать ключевые точки целевого изображения, после того как отпускает мышь происходит перерендеринг целевого изображения через мой класс DisplacementField
5. пользователь может изменять прозрачность целевого изображения
6. текущие ключевые точки целевого изображения всегда отображаются поверх всех изображений
7. при нажатии на кнопку Готово нужно сохранить отношение ключевых точек фонового лица к ключевым точкам целевого лица в json файл.

import dlib
import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import json
from common.editor import *

# Загрузка модели для детектирования ключевых точек лица
predictor_path = "assets/cache/shape_predictor_68_face_landmarks.dat"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(predictor_path)

def get_landmarks(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    if len(faces) == 0:
        raise ValueError("No faces detected in the image")
    landmarks = predictor(gray, faces[0])
    return np.array([(landmarks.part(i).x, landmarks.part(i).y) for i in range(landmarks.num_parts)])

class LandmarkApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Landmark Editor")
        self.canvas = tk.Canvas(root, width=800, height=600, bg="white")
        self.canvas.pack()

        self.background_image = None
        self.target_image = None
        self.landmarks = None
        self.selected_point = None
        self.original_landmarks = None
        self.background_landmarks = None
        self.scale_factor_background = 1.0
        self.scale_factor_target = 1.0
        self.buffered_target_image = None
        self.current_alpha = 100
        self.target_offset_size = 1.0

        self.load_images()
        self.draw_images()
        self.detect_landmarks()
        self.align_faces()  # Вызываем метод для выравнивания лиц
        self.draw_landmarks()

        self.canvas.bind("<Button-1>", self.on_click)
        self.canvas.bind("<B1-Motion>", self.on_motion)
        self.canvas.bind("<ButtonRelease-1>", self.on_release)

        self.save_button = tk.Button(root, text="Готово", command=self.save_landmarks)
        self.save_button.pack(side=tk.BOTTOM)

        # Добавляем ползунок для регулировки прозрачности
        self.alpha_slider = tk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, label="Прозрачность", command=self.update_alpha)
        self.alpha_slider.set(100)  # Начальное значение ползунка
        self.alpha_slider.pack(side=tk.BOTTOM)

        # Добавляем ползунок для регулировки target_offset_size
        self.offset_slider = tk.Scale(root, from_=0.1, to=2.0, resolution=0.1, orient=tk.HORIZONTAL, label="Размер целевого изображения", command=self.update_offset_size)
        self.offset_slider.set(1.0)  # Начальное значение ползунка
        self.offset_slider.pack(side=tk.BOTTOM)

    def load_images(self):
        background_path = filedialog.askopenfilename(title="Select Background Image")
        target_path = filedialog.askopenfilename(title="Select Target Image")

        self.background_image = cv2.imread(background_path)
        self.target_image = cv2.imread(target_path)

        self.scale_factor_background = min(800 / self.background_image.shape[1], 600 / self.background_image.shape[0])
        self.background_image = cv2.resize(self.background_image, None, fx=self.scale_factor_background, fy=self.scale_factor_background)

        self.scale_factor_target = min(800 / self.target_image.shape[1], 600 / self.target_image.shape[0])
        self.target_image = cv2.resize(self.target_image, None, fx=self.scale_factor_target, fy=self.scale_factor_target)
        self.buffered_target_image = self.target_image.copy()

    def draw_images(self):
        self.canvas.delete("all")
        self.background_photo = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(self.background_image, cv2.COLOR_BGR2RGB)))
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.background_photo)

        if self.buffered_target_image is not None:
            self.target_photo = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(self.buffered_target_image, cv2.COLOR_BGR2RGB)))
        else:
            self.target_photo = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(self.target_image, cv2.COLOR_BGR2RGB)))
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.target_photo)

    def detect_landmarks(self):
        self.landmarks = get_landmarks(self.target_image)
        self.background_landmarks = get_landmarks(self.background_image)

        self.original_landmarks = self.landmarks.copy()

    def draw_landmarks(self):
        self.canvas.delete("landmarks")
        for (x, y) in self.landmarks:
            self.canvas.create_oval(x-2, y-2, x+2, y+2, fill="red", tags="landmarks")

    def on_click(self, event):
        self.selected_point = self.find_closest_point(event.x, event.y, self.landmarks)

    def on_motion(self, event):
        if self.selected_point is not None:
            self.landmarks[self.selected_point] = (event.x, event.y)
            self.draw_landmarks()
    
    def on_release(self, event):
        self.selected_point = None

        # Список исходных ключевых точек для целевого изображения
        base_landmarks = self.original_landmarks.copy()

        # Список измененных пользователем (текущий) ключевых точек для целевого изображения
        target_landmarks = self.landmarks.copy()

        interpolation_kwargs = {"function": "linear"}

        df = DisplacementField.generate(
            self.target_image.shape[:2],
            base_landmarks,
            target_landmarks,
            anchor_edges=True,
            **interpolation_kwargs
        )

        self.buffered_target_image = df.warp(self.target_image)
        self.update_alpha(self.current_alpha)

    def find_closest_point(self, x, y, landmarks):
        distances = np.linalg.norm(landmarks - np.array([x, y]), axis=1)
        return np.argmin(distances)

    def save_landmarks(self):
        original_landmarks_scaled = (self.original_landmarks / self.scale_factor_target).astype(int).tolist()
        modified_landmarks_scaled = (self.landmarks / self.scale_factor_target).astype(int).tolist()

        original_data = {"landmarks": original_landmarks_scaled}
        modified_data = {"landmarks": modified_landmarks_scaled}

        with open("original_landmarks.json", "w") as f:
            json.dump(original_data, f)

        with open("modified_landmarks.json", "w") as f:
            json.dump(modified_data, f)

        # Вычисляем отношение координат фонового изображения к текущему целевому изображению
        background_landmarks_scaled = (self.background_landmarks / self.scale_factor_background).astype(int).tolist()
        target_landmarks_scaled = (self.landmarks / self.scale_factor_target).astype(int).tolist()

        coordinate_ratios = []
        for bg_landmark, tg_landmark in zip(background_landmarks_scaled, target_landmarks_scaled):
            ratio_x = bg_landmark[0] / tg_landmark[0] if tg_landmark[0] != 0 else 1.0
            ratio_y = bg_landmark[1] / tg_landmark[1] if tg_landmark[1] != 0 else 1.0
            coordinate_ratios.append((ratio_x, ratio_y))

        results_data = {"coordinate_ratios": coordinate_ratios}

        with open("results.json", "w") as f:
            json.dump(results_data, f)

        messagebox.showinfo("Сохранение", "Координаты точек сохранены в original_landmarks.json, modified_landmarks.json и results.json")
        
    def update_alpha(self, value):
        self.current_alpha = value
        value = int(value) / 100.0
        overlay = cv2.cvtColor(self.buffered_target_image, cv2.COLOR_BGR2RGB)
        overlay = Image.fromarray(overlay)
        overlay = overlay.convert("RGBA")
        datas = overlay.getdata()

        new_data = []
        for item in datas:
            new_data.append((item[0], item[1], item[2], int(item[3] * value)))

        overlay.putdata(new_data)
        self.target_photo = ImageTk.PhotoImage(overlay)
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.target_photo)

    def transform_target_image(self, base_landmarks, target_landmarks):
        interpolation_kwargs = {"function": "linear"}

        df = DisplacementField.generate(
            self.target_image.shape[:2],
            base_landmarks,
            target_landmarks,
            anchor_edges=True,
            **interpolation_kwargs
        )

        self.buffered_target_image = df.warp(self.target_image)
        self.update_alpha(self.current_alpha)

    def align_faces(self):
        if self.landmarks is None or len(self.landmarks) == 0:
            return

        # Вычисляем смещение для совмещения носов
        background_nose = self.background_landmarks[30]
        target_nose = self.landmarks[30]
        nose_offset = (background_nose[0] - target_nose[0], background_nose[1] - target_nose[1])

        # Применяем смещение к ключевым точкам целевого изображения
        self.landmarks = self.landmarks + np.array(nose_offset)

        # Вычисляем масштабирование для совмещения лиц
        background_bbox = cv2.boundingRect(self.background_landmarks)
        target_bbox = cv2.boundingRect(self.landmarks)

        scale_x = background_bbox[2] / target_bbox[2]
        scale_y = background_bbox[3] / target_bbox[3]
        scale = min(scale_x, scale_y)

        # Применяем масштабирование к ключевым точкам целевого изображения и приводим к типу int
        self.landmarks = ((self.landmarks - target_nose) * scale * self.target_offset_size + target_nose).astype(int)

        # Применяем смещение и масштабирование к целевому изображению
        M = np.float32([[scale * self.target_offset_size, 0, nose_offset[0]], [0, scale * self.target_offset_size, nose_offset[1]]])
        self.buffered_target_image = cv2.warpAffine(self.target_image, M, (self.target_image.shape[1], self.target_image.shape[0]))

        # Обновляем изображение на холсте
        self.draw_images()

    def update_offset_size(self, value):
        self.target_offset_size = float(value)
        self.align_faces()

if __name__ == "__main__":
    root = tk.Tk()
    app = LandmarkApp(root)
    root.mainloop()