Вот мой код:
```py
# 2. Импорт необходимых библиотек
import cv2
import mediapipe as mp
import numpy as np
import math
from collections import deque
from google.colab import files
import tempfile
import os
from IPython.display import HTML
from base64 import b64encode
import zipfile
import io

# 3. Загрузка и распаковка папки DeepFit и видеофайла
print("Пожалуйста, загрузите ZIP-архив папки DeepFit и видеофайл (например, input_video.mp4).")
uploaded = files.upload()

# Предполагается, что были загружены:
# - deepfit.zip (содержит папку DeepFit)
# - input_video.mp4

# Разархивируем DeepFit
for file in uploaded.keys():
    if file.endswith('.zip'):
        print(f"Распаковка {file}...")
        with zipfile.ZipFile(io.BytesIO(uploaded[file]), 'r') as zip_ref:
            zip_ref.extractall('.')
        print("Распаковка завершена.")

# Поиск видеофайла среди загруженных файлов
video_extensions = ['.mp4', '.avi', '.mov', '.mkv']
video_files = [filename for filename in uploaded.keys() if any(filename.lower().endswith(ext) for ext in video_extensions)]

if not video_files:
    raise ValueError("Не найдено загруженных видеофайлов. Пожалуйста, загрузите видеофайл с расширением .mp4, .avi, .mov или .mkv")
else:
    video_path = video_files[0]
    print(f"Используется видеофайл: {video_path}")

# Проверка наличия необходимого содержимого в папке DeepFit
required_files = ['DeepFitClassifier.py', 'deepfit_classifier_v3.tflite']
for req_file in required_files:
    deepfit_path = os.path.join('DeepFit', req_file)
    if not os.path.exists(deepfit_path):
        raise FileNotFoundError(f"Не найден файл: {deepfit_path}. Пожалуйста, убедитесь, что все необходимые файлы находятся в папке DeepFit.")

# Добавление папки DeepFit в системный путь для импорта
import sys
sys.path.append('DeepFit')

# Импорт DeepFitClassifier после загрузки и распаковки
from DeepFitClassifier import DeepFitClassifier

# 4. Определение всех необходимых функций и параметров

# Mapping dictionary to map keypoints from Mediapipe to our Classifier model
lm_dict = {
    0: 0, 1: 10, 2: 12, 3: 14, 4: 16, 5: 11, 6: 13, 7: 15, 8: 24, 9: 26,
    10: 28, 11: 23, 12: 25, 13: 27, 14: 5, 15: 2, 16: 8, 17: 7,
}

def set_pose_parameters():
    mode = False
    complexity = 1
    smooth_landmarks = True
    enable_segmentation = False
    smooth_segmentation = True
    detectionCon = 0.5
    trackCon = 0.5
    mpPose = mp.solutions.pose
    return mode, complexity, smooth_landmarks, enable_segmentation, smooth_segmentation, detectionCon, trackCon, mpPose

def get_pose(img, results, draw=True):
    if results.pose_landmarks:
        if draw:
            mpDraw = mp.solutions.drawing_utils
            mpDraw.draw_landmarks(img, results.pose_landmarks, mpPose.POSE_CONNECTIONS)
    return img

def get_position(img, results, height, width, draw=True):
    landmark_list = []
    if results.pose_landmarks:
        for id, landmark in enumerate(results.pose_landmarks.landmark):
            # finding height, width of the image printed
            landmark_pixel_x, landmark_pixel_y = int(landmark.x * width), int(landmark.y * height)
            landmark_list.append([id, landmark_pixel_x, landmark_pixel_y])
            if draw:
                cv2.circle(img, (landmark_pixel_x, landmark_pixel_y), 5, (255, 0, 0), cv2.FILLED)
    return landmark_list

def get_angle(img, landmark_list, point1, point2, point3, draw=True):
    # Retrieve landmark coordinates from point identifiers
    try:
        x1, y1 = landmark_list[point1][1:]
        x2, y2 = landmark_list[point2][1:]
        x3, y3 = landmark_list[point3][1:]
    except IndexError:
        return 0  # Возвращать 0, если ключевая точка отсутствует

    angle = math.degrees(math.atan2(y3 - y2, x3 - x2) -
                         math.atan2(y1 - y2, x1 - x2))

    # Handling angle edge cases: Obtuse and negative angles
    if angle < 0:
        angle += 360
        if angle > 180:
            angle = 360 - angle
    elif angle > 180:
        angle = 360 - angle

    if draw:
        # Drawing lines between the three points
        cv2.line(img, (x1, y1), (x2, y2), (255,255,255), 3)
        cv2.line(img, (x3, y3), (x2, y2), (255,255,255), 3)

        # Drawing circles at intersection points of lines
        cv2.circle(img, (x1, y1), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x1, y1), 15, (75,0,130), 2)
        cv2.circle(img, (x2, y2), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x2, y2), 15, (75,0,130), 2)
        cv2.circle(img, (x3, y3), 5, (75,0,130), cv2.FILLED)
        cv2.circle(img, (x3, y3), 15, (75,0,130), 2)

        # Show angles between lines
        cv2.putText(img, str(int(angle)), (x2-50, y2+50),
                    cv2.FONT_HERSHEY_PLAIN, 2, (0,0,255), 2)
    return angle

def convert_mediapipe_keypoints_for_model(lm_dict, landmark_list):
    inp_pushup = []
    for index in range(0, 36):
        if index < 18:
            try:
                inp_pushup.append(round(landmark_list[lm_dict[index]][1], 3))
            except IndexError:
                inp_pushup.append(0.0)
        else:
            try:
                inp_pushup.append(round(landmark_list[lm_dict[index - 18]][2], 3))
            except IndexError:
                inp_pushup.append(0.0)
    return inp_pushup

# Предположим, что вы хотите использовать классификатор DeepFit для определения типа упражнения
# Если этот функционал не нужен, можно проигнорировать соответствующие части кода

def set_video_feed_variables(video_path):
    # Используем загруженный видеофайл
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise IOError(f"Не удалось открыть видеофайл: {video_path}")
    count = 0
    direction = 0
    form = 0
    feedback = "Bad Form. Correct Posture."
    frame_queue = deque(maxlen=250)
    clf = DeepFitClassifier('DeepFit/deepfit_classifier_v3.tflite')  # Убедитесь, что путь корректный
    return cap, count, direction, form, feedback, frame_queue, clf

def main():
    # Настройка параметров позы
    mode, complexity, smooth_landmarks, enable_segmentation, smooth_segmentation, detectionCon, trackCon, mpPose = set_pose_parameters()
    pose = mpPose.Pose(mode, complexity, smooth_landmarks,
                       enable_segmentation, smooth_segmentation,
                       detectionCon, trackCon)

    # Настройка видеофида
    cap, count, direction, form, feedback, frame_queue, clf = set_video_feed_variables(video_path)

    # Настройка параметров видео для сохранения результата
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    output_filename = 'output_video.mp4'
    fps = cap.get(cv2.CAP_PROP_FPS)
    if fps == 0:
        fps = 30  # Значение по умолчанию, если не удалось получить FPS
        print("Не удалось определить FPS видео. Используется значение по умолчанию: 30")
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    out = cv2.VideoWriter(output_filename, fourcc, fps, (width, height))

    frame_count = 0
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(f"Total frames in input video: {total_frames}")

    while cap.isOpened():
        # Получение кадра из видео
        ret, img = cap.read()
        if not ret:
            break
        frame_count += 1
        print(f"Processing frame {frame_count} of {total_frames}", end='\r')

        # Конвертация из BGR (используемого cv2) в RGB (используемого Mediapipe)
        results = pose.process(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

        # Получение позы и рисование ключевых точек
        img = get_pose(img, results, False)

        # Получение списка ключевых точек
        landmark_list = get_position(img, results, height, width, False)

        # Если ключевые точки существуют, получить соответствующие углы и выполнить подсчёт повторений
        if len(landmark_list) != 0:
            elbow_angle = get_angle(img, landmark_list, 11, 13, 15)
            shoulder_angle = get_angle(img, landmark_list, 13, 11, 23)
            hip_angle = get_angle(img, landmark_list, 11, 23, 25)

            # Интерполяция угла локтя для прогресс-бара и процента успешности
            pushup_success_percentage = np.interp(elbow_angle, (90, 160), (0, 100))
            pushup_progress_bar = np.interp(elbow_angle, (90, 160), (380, 50))

            # Проверка начальной формы
            if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                form = 1

            # Полный цикл выполнения отжиманий
            if form == 1:
                if pushup_success_percentage == 0:
                    if elbow_angle <= 90 and hip_angle > 160:
                        feedback = "Go Up"
                        if direction == 0:
                            count += 0.5
                            direction = 1
                    else:
                        feedback = "Bad Form. Correct Posture."

                if pushup_success_percentage == 100:
                    if elbow_angle > 160 and shoulder_angle > 40 and hip_angle > 160:
                        feedback = "Go Down"
                        if direction == 1:
                            count += 0.5
                            direction = 0
                    else:
                        feedback = "Bad Form. Correct Posture."

            print(f"Reps: {int(count)}")

            # Рисование прогресс-бара отжиманий
            if form == 1:
                cv2.rectangle(img, (580, 50), (600, 380), (0, 255, 0), 3)
                cv2.rectangle(img, (580, int(pushup_progress_bar)), (600, 380), (0, 255, 0), cv2.FILLED)
                cv2.putText(img, f'{int(pushup_success_percentage)}%', (565, 430), cv2.FONT_HERSHEY_PLAIN, 2,
                            (255, 0, 0), 2)

            # Отображение счёта отжиманий
            cv2.rectangle(img, (0, 380), (100, 480), (0, 255, 0), cv2.FILLED)
            cv2.putText(img, str(int(count)), (25, 455), cv2.FONT_HERSHEY_PLAIN, 5,
                        (255, 0, 0), 5)

            # Отображение обратной связи
            cv2.rectangle(img, (500, 0), (640, 40), (255, 255, 255), cv2.FILLED)
            cv2.putText(img, feedback, (500, 40 ), cv2.FONT_HERSHEY_PLAIN, 2,
                        (0, 255, 0), 2)

        # Запись аннотированного кадра в выходной файл
        out.write(img)

    # Завершение обработки видео
    cap.release()
    out.release()
    print("\nProcessing complete.")

    # Функция для отображения видео в Colab
    def display_video(video_path):
        mp4 = open(video_path, 'rb').read()
        data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
        return HTML(f'''
            <video width="800" height="600" controls>
                <source src="{data_url}" type="video/mp4">
            </video>
        ''')

    # Проверка наличия выходного файла
    if os.path.exists(output_filename):
        # Отображение выходного видео
        display_video(output_filename)
    else:
        print(f"Не удалось создать выходной файл видео: {output_filename}")

if __name__ == "__main__":
    main()
```


Принцип его работы:
## Отчет по коду подсчета отжиманий

Код анализирует видео, подсчитывая выполненные отжимания с помощью Mediapipe (отслеживание поз) и TensorFlow Lite (классификация упражнений, хотя в текущей версии не используется).

**Ключевые этапы:**

1. **Инициализация:** Загрузка модели DeepFit, видео, настройка Mediapipe.

2. **Обработка кадров:** Цикл по кадрам видео.
    * Конвертация кадра в RGB.
    * Обнаружение поз с помощью Mediapipe.
    * Извлечение координат ключевых точек.

3. **Подсчет отжиманий:**
    * Расчет углов в локте, плече и бедре.
    * Определение "фазы" отжимания (вверх/вниз) на основе углов и пороговых значений.
    * Подсчет повторений при переходе между фазами.

4. **Визуализация:**
    * Отображение на кадре: подсчитанных повторений, текущей фазы ("вверх"/"вниз"), прогресс-бара выполнения отжимания,  обратной связи по форме выполнения.

5. **Сохранение результатов:** Запись обработанного видео с визуализацией в файл `output_video.mp4`.


**Основные функции:**

* `get_angle()`:  Вычисляет угол между тремя ключевыми точками.
* `get_position()`: Извлекает координаты ключевых точек.
* `set_video_feed_variables()`: Инициализирует переменные для обработки видео.


**Упрощения:**

* Классификатор DeepFit загружается, но не используется.
* Очередь кадров (`frame_queue`) объявлена, но не используется.

**Вывод:** Код выполняет подсчет отжиманий на основе анализа углов в суставах.  Для улучшения точности можно использовать классификатор DeepFit для определения типа упражнения.




Можешь сделать полный рефакторинг кода, разбив его на разные файлы.
Преврати его в класс, чтобы его использовать можно было просто как модуль в несколько строк кода.
Отрисовку сделай в отдельном классе, т.к. она не обязательная. Из модуля определения мы будем получать всю необходимую информацию, чтобы потом передать ее в модуль отрисовки.
Распиши принцип работы отрисовки, т.к. я потом в будущем хочу превратить это в веб сервер и обрисовывать все на фронтенде на html, css, js.
Этого пока не делай, просто распиши подробно как работают данные, которые вернет модуль классификации.