import cv2
import numpy as np
import time
from scipy.signal import find_peaks
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# Константы
BUFFER_SIZE = 150
BPM_LOWER_LIMIT = 40.0
BPM_UPPER_LIMIT = 180.0

class PulseDetector:
    def __init__(self):
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        if self.face_cascade.empty():
            raise ValueError("Ошибка при загрузке каскада Хаара для обнаружения лиц")
        self.data_buffer = []
        self.times = []
        self.bpm = 0.0
        self.start_time = time.time()

    def detect_pulse(self, frame):
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
        
        if len(faces) == 0:
            print("Лицо не обнаружено")
            return None

        (x, y, w, h) = faces[0]
        forehead_top = int(y + h * 0.1)
        forehead_bottom = int(y + h * 0.3)
        forehead_left = int(x + w * 0.3)
        forehead_right = int(x + w * 0.7)

        forehead = frame[forehead_top:forehead_bottom, forehead_left:forehead_right]
        
        if forehead.size == 0:
            print("Область лба имеет нулевой размер")
            return None

        green_channel = forehead[:,:,1]
        mean_green = np.mean(green_channel)

        current_time = time.time() - self.start_time
        self.times.append(current_time)
        self.data_buffer.append(mean_green)

        if len(self.data_buffer) > BUFFER_SIZE:
            self.data_buffer.pop(0)
            self.times.pop(0)

        if len(self.data_buffer) == BUFFER_SIZE:
            self.calculate_bpm()

        cv2.rectangle(frame, (forehead_left, forehead_top), (forehead_right, forehead_bottom), (0, 255, 0), 2)
        cv2.putText(frame, f"BPM: {self.bpm:.1f}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
        
        return self.bpm

    def calculate_bpm(self):
        if len(self.times) < 2:
            print("Недостаточно данных для расчета BPM")
            return

        fps = len(self.data_buffer) / (self.times[-1] - self.times[0])
        if fps <= 0:
            print(f"Некорректное значение FPS: {fps}")
            return

        # Нормализация данных
        normalized_data = np.array(self.data_buffer)
        normalized_data = (normalized_data - np.mean(normalized_data)) / np.std(normalized_data)
        
        # Поиск пиков
        peaks, _ = find_peaks(normalized_data, distance=int(fps * 0.5))
        
        if len(peaks) >= 2:
            peak_intervals = np.diff(peaks) / fps
            bpm_values = 60 / peak_intervals
            self.bpm = np.median(bpm_values)
            
            if self.bpm < BPM_LOWER_LIMIT or self.bpm > BPM_UPPER_LIMIT:
                print(f"BPM вне допустимого диапазона: {self.bpm}")
                self.bpm = 0.0
        else:
            print(f"Недостаточно пиков для расчета BPM. Найдено пиков: {len(peaks)}")

def main():
    detector = PulseDetector()
    cap = cv2.VideoCapture(0)

    if not cap.isOpened():
        print("Ошибка при открытии камеры")
        return

    root = tk.Tk()
    root.title("Определение пульса")
    
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 10))
    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas_widget = canvas.get_tk_widget()
    canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    bpm_history = []
    time_history = []

    def update_plot(frame):
        ret, frame = cap.read()
        if not ret:
            print("Не удалось получить кадр")
            return

        bpm = detector.detect_pulse(frame)

        ax1.clear()
        ax1.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        ax1.axis('off')
        
        if bpm is not None:
            bpm_history.append(bpm)
            time_history.append(time.time() - detector.start_time)
            
            if len(bpm_history) > 100:
                bpm_history.pop(0)
                time_history.pop(0)
            
            ax2.clear()
            ax2.plot(time_history, bpm_history, 'b-')
            ax2.set_xlabel('Время (с)')
            ax2.set_ylabel('BPM')
            ax2.set_ylim(BPM_LOWER_LIMIT, BPM_UPPER_LIMIT)
            ax2.set_title('История измерений BPM')

        canvas.draw()

    ani = FuncAnimation(fig, update_plot, interval=50, cache_frame_data=False)

    def on_closing():
        cap.release()
        root.quit()
        root.destroy()

    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    main() исправь код