├── .gitignore
├── README.md
├── cvplayer
├── __init__.py
├── core
│ ├── __init__.py
│ ├── media_objects
│ │ ├── __init__.py
│ │ ├── image.py
│ │ └── video.py
│ ├── media_player
│ │ ├── __init__.py
│ │ ├── default.mp4
│ │ ├── image_player_core
│ │ │ ├── back_end
│ │ │ │ └── image_player.py
│ │ │ └── interface
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_images_buttons.py
│ │ │ │ ├── icons
│ │ │ │ ├── ScreenShot.png
│ │ │ │ ├── White.png
│ │ │ │ ├── add.png
│ │ │ │ ├── add_hover.png
│ │ │ │ ├── add_pressed.png
│ │ │ │ ├── black.png
│ │ │ │ ├── cvplayerlogo.png
│ │ │ │ ├── drop_down.png
│ │ │ │ ├── next.png
│ │ │ │ ├── next2.png
│ │ │ │ ├── pause.png
│ │ │ │ ├── play.png
│ │ │ │ ├── previous.png
│ │ │ │ ├── previous2.png
│ │ │ │ └── solid-color-image.png
│ │ │ │ ├── image_viewer.py
│ │ │ │ ├── images_counter.py
│ │ │ │ ├── images_list.py
│ │ │ │ └── media_buttons.py
│ │ ├── image_player_widget.py
│ │ ├── video_player_core
│ │ │ ├── __init__.py
│ │ │ ├── back_end
│ │ │ │ ├── video_player.py
│ │ │ │ └── videos_manager.py
│ │ │ └── interface
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_videos_button.py
│ │ │ │ ├── icons
│ │ │ │ ├── ScreenShot.png
│ │ │ │ ├── White.png
│ │ │ │ ├── add.png
│ │ │ │ ├── add_hover.png
│ │ │ │ ├── add_pressed.png
│ │ │ │ ├── black.png
│ │ │ │ ├── cvplayerlogo.png
│ │ │ │ ├── drop_down.png
│ │ │ │ ├── next.png
│ │ │ │ ├── next2.png
│ │ │ │ ├── pause.png
│ │ │ │ ├── play.png
│ │ │ │ ├── previous.png
│ │ │ │ ├── previous2.png
│ │ │ │ └── solid-color-image.png
│ │ │ │ ├── media_buttons.py
│ │ │ │ ├── print_screen_button.py
│ │ │ │ ├── slider.py
│ │ │ │ ├── video_speed_button.py
│ │ │ │ ├── video_time_counter.py
│ │ │ │ ├── videos_list.py
│ │ │ │ └── viewer.py
│ │ └── video_player_widget.py
│ ├── references.txt
│ ├── updater
│ │ ├── hybrid_updater.py
│ │ ├── old_video_updater.py
│ │ ├── time_updater.py
│ │ ├── updater.py
│ │ └── video_updater.py
│ └── utils
│ │ ├── json_utils.py
│ │ ├── layout_utils.py
│ │ ├── stylesheet_utils.py
│ │ ├── stylesheets
│ │ ├── add_videos_button.css
│ │ ├── image_counter.css
│ │ ├── image_player_widget.css
│ │ ├── image_viewer.css
│ │ ├── next_frame_button.css
│ │ ├── next_image_button.css
│ │ ├── pause_button.css
│ │ ├── play_button.css
│ │ ├── previous_frame_button.css
│ │ ├── previous_image_button.css
│ │ ├── print_screen_button.css
│ │ ├── video_player_widget.css
│ │ ├── video_slider.css
│ │ ├── video_speed_button.css
│ │ ├── video_time_counter.css
│ │ └── videos_list.css
│ │ ├── time_and_date_utils.py
│ │ ├── video_utils.py
│ │ └── widgets_utils.py
├── docker
│ ├── Dockerfile
│ └── runing_docker.txt
├── icons
│ ├── ScreenShot.png
│ ├── White.png
│ ├── add.png
│ ├── add_hover.png
│ ├── add_pressed.png
│ ├── black.png
│ ├── cvplayerlogo.png
│ ├── drop_down.png
│ ├── example.png
│ ├── example2.mp4
│ ├── example2.png
│ ├── example3.mp4
│ ├── example3.png
│ ├── example4.png
│ ├── example5.png
│ ├── next.png
│ ├── next2.png
│ ├── pause.png
│ ├── play.png
│ ├── previous.png
│ ├── previous2.png
│ └── solid-color-image.png
├── image_player.py
├── stylesheets
│ ├── add_videos_button.css
│ ├── image_counter.css
│ ├── image_player_widget.css
│ ├── image_viewer.css
│ ├── next_frame_button.css
│ ├── next_image_button.css
│ ├── pause_button.css
│ ├── play_button.css
│ ├── previous_frame_button.css
│ ├── previous_image_button.css
│ ├── print_screen_button.css
│ ├── video_player_widget.css
│ ├── video_slider.css
│ ├── video_speed_button.css
│ ├── video_time_counter.css
│ └── videos_list.css
└── video_player.py
├── demo_mmdetection.py
├── demo_yolov8.py
├── requirements.txt
├── setup.py
└── tutorial.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | bin/
10 | build/
11 | develop-eggs/
12 | dist/
13 | eggs/
14 | lib/
15 | lib64/
16 | parts/
17 | sdist/
18 | var/
19 | *.egg-info/
20 | .installed.cfg
21 | *.egg
22 |
23 | # Installer logs
24 | pip-log.txt
25 | pip-delete-this-directory.txt
26 |
27 | # Unit test / coverage reports
28 | .tox/
29 | .coverage
30 | .cache
31 | nosetests.xml
32 | coverage.xml
33 |
34 | # Translations
35 | *.mo
36 |
37 | # Mr Developer
38 | .mr.developer.cfg
39 | .project
40 | .pydevproject
41 |
42 | # Rope
43 | .ropeproject
44 |
45 | # Django stuff:
46 | *.log
47 | *.pot
48 |
49 | # Sphinx documentation
50 | docs/_build/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
cvplayer
3 |
4 |
5 |
6 | Universal media player for computer vision models and their inferences
7 |
8 |
9 |
10 |
11 | Examples of the video and image player with detection and segmentation models
12 |
13 | ## Description
14 |
15 | Welcome to **CVPlayer**, a powerful and easy to use tool for visualizing computer vision models inferences.
16 | Tired of writing long codes just to visualize your model's output? With CVPlayer, you can easily analyze the output of your models with just a few lines of code, no mather wich library or framework you are using, saving you from long codes for a simple inference visualization and ofering you an a lot better experience over the traditional way of doing it.
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## Features
24 | - [x] Video inference visualization.
25 | - [x] Images inference visualization.
26 | - [x] Compatible with any computer vision framework.
27 | - [x] Frame level control.
28 | - [x] Easy to use.
29 | - [ ] Portable for PyQt6 aplications.
30 | - [ ] Manipulate the player by an internal API.
31 |
32 |
33 | ## Requirements
34 |
35 | ### pip
36 |
37 | ```bash
38 | # inside your env
39 | pip install PyQt6==6.4.2 PyQt6-Qt6==6.4.2 PyQt6-sip==13.4.1
40 | pip install opencv-python
41 | pip install pillow
42 | ```
43 |
44 | ## Instalation
45 |
46 | ```bash
47 | # inside your env
48 | git clone https://github.com/edu010101/cvplayer
49 | cd cvplayer
50 | pip install -e .
51 | ```
52 | ## Usage
53 |
54 | ### Tutorials
55 | A colab tutorial for getting started with cvplayer [](tutorial.ipynb)
56 |
57 | ### Controls
58 | - Play/Pause: Space Bar (⎵).
59 | - Next Frame/Image: Right Key (→).
60 | - Previous Frame/Image: Left Key (←).
61 | - Change Video Speed: Up and Down Keys (↑,↓).
62 |
63 | ### Video Player
64 |
65 | ```python
66 | from cvplayer import VideoPlayer
67 |
68 | #create a class with any name
69 | class ExampleName():
70 | def __init___(self): #it can have many args as you need
71 | #initialize your model
72 | self.model = 'path/to/your/model'
73 |
74 | #your class NEED to have this method with this exactly name.
75 | def custom_method(self, numpy_image): #this method can have just the np.array parameter
76 | #this method receives an np.array representing your image or frame
77 | #then modify and inference your array as you want
78 |
79 | return numpy_image #then return your modified np.array
80 |
81 | VideoPlayer(ExampleName()) #load your class as an argument to the player
82 | ```
83 |
84 | ### Yolov8 demo
85 |
86 | ```python
87 | from cvplayer import VideoPlayer
88 | from ultralytics import YOLO
89 | import cv2
90 |
91 | #yolov8 example
92 | class Yolov8():
93 | def __init__(self) -> None:
94 | self.model = YOLO("yolov8n.pt") # load a pretrained model
95 |
96 | def custom_method(self, image): #method to be called on each frame and do whatever you want
97 | results = self.model(image) # predict on an image
98 | for result in results:
99 | boxes = result.boxes # Boxes object for bbox outputs
100 | for box in boxes:
101 | cv2.rectangle(image, (int(box.xyxy[0][0]), int(box.xyxy[0][1])), (int(box.xyxy[0][2]), int(box.xyxy[0][3])), (0, 255, 0), 2)
102 |
103 | return image #return the image with the changes
104 |
105 | VideoPlayer(Yolov8()) #pass the class to the VideoPlayer and start the player
106 | ```
107 |
108 | ### Image Player
109 |
110 | ```python
111 | from cvplayer import ImagePlayer
112 |
113 | #create a class with any name
114 | class ExampleClass():
115 | def __init___(self): #it can have many args as you need
116 | #initialize your model
117 | self.model = 'path/to/your/model'
118 |
119 | #your class NEED to have this method with this exactly name.
120 | def custom_method(self, numpy_image):
121 | #this method receives an np.array representing your image or frame
122 | #modify and inference your array as you want
123 |
124 | return numpy_image #then return your modified np.array
125 |
126 | imageclassifier = ExampleClass()
127 | ImagePlayer(imageclassifier) #load your class as an argument to the player
128 | ```
129 |
130 | ### mmdetection demo
131 |
132 | ```python
133 | from cvplayer import ImagePlayer
134 | from mmdet.apis import inference_detector, init_detector
135 |
136 | #mmdectection example
137 | class FasterRCNN():
138 | def __init__(self) -> None: #always load the model in the constructor
139 | model_config='your_path_to_config/faster_rcnn_r50_fpn_1x.py' #you can use any model from mmdetection
140 | model_weights='your_path_to_weights/epoch_50.pth'
141 | self.detection_model = init_detector(model_config, model_weights, device='cuda:0')
142 |
143 | def custom_method(self, numpy_image):
144 | detection_result = inference_detector(self.detection_model, numpy_image)
145 | return self.detection_model.show_result(numpy_image, detection_result, score_thr=0.7, show=False)
146 |
147 | ImagePlayer(FasterRCNN()) #pass the class to the ImagePlayer and start the player
148 |
149 | ```
150 |
151 |
--------------------------------------------------------------------------------
/cvplayer/__init__.py:
--------------------------------------------------------------------------------
1 | from cvplayer.video_player import VideoPlayer
2 | from cvplayer.image_player import ImagePlayer
--------------------------------------------------------------------------------
/cvplayer/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/__init__.py
--------------------------------------------------------------------------------
/cvplayer/core/media_objects/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_objects/__init__.py
--------------------------------------------------------------------------------
/cvplayer/core/media_objects/image.py:
--------------------------------------------------------------------------------
1 | import cv2
2 |
3 | class Image():
4 | def __init__(self, image_path:str) -> None:
5 | self.image = cv2.imread(image_path)
6 |
7 | def get_image(self):
8 | return self.image
9 |
--------------------------------------------------------------------------------
/cvplayer/core/media_objects/video.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | from cvplayer.core.utils.video_utils import calculate_time_beetwen_frames
3 |
4 | MINIMAL_FLOAT = 0.0000001
5 |
6 | class Video:
7 | current_frame = None
8 | current_frame_id = 0
9 | current_milliseconds = 0
10 | current_seconds = 0
11 | future_frame_id = 0
12 |
13 | def __init__(self, video_path: str, frames_to_jump: int = 1) -> None:
14 | self.video = cv2.VideoCapture(video_path)
15 | self.frames_to_jump = frames_to_jump
16 | self.fps = self.get_video_fps()
17 | self.total_number_of_frames = self.get_total_number_of_frames()
18 |
19 | def set_current_frame_id(self, frame_id: int) -> None:
20 | self.future_frame_id = frame_id
21 |
22 | def set_current_frame_position_by_index(self) -> None:
23 | self.video.set(cv2.CAP_PROP_POS_FRAMES, self.future_frame_id)
24 |
25 | def set_frames_to_jump(self, frames_to_jump: int) -> None:
26 | self.frames_to_jump = frames_to_jump
27 |
28 | def grab_encoded_next_frame(self) -> None:
29 | self.video.grab()
30 |
31 | def decode_current_frame(self) -> None:
32 | _, self.current_frame = self.video.retrieve()
33 |
34 | def update_to_next_frame(self) -> None:
35 | _, self.current_frame = self.video.read()
36 |
37 | def update_current_frame_id(self) -> None:
38 | self.current_frame_id = self.get_current_frame_id()
39 |
40 | def update_video_time(self) -> None:
41 | self.current_milliseconds = self.get_milliseconds()
42 | self.current_seconds = self.get_seconds()
43 |
44 | def update_to_next_frame_based_on_frames_to_jump(self) -> None:
45 | for _ in range(self.frames_to_jump):
46 | self.grab_encoded_next_frame()
47 |
48 | self.decode_current_frame()
49 |
50 | #Basic a union of the last 3 methods
51 | def update_to_next_video_moment(self) -> None:
52 | """Iterates to the next frame and updates the video parameters"""
53 | if self.current_frame_id + self.frames_to_jump >= self.total_number_of_frames:
54 | self.set_current_frame_id(0)
55 | self.update_to_specific_video_moment()
56 |
57 | self.update_to_next_frame_based_on_frames_to_jump()
58 | self.update_current_frame_id()
59 | self.update_video_time()
60 | # print('Current frame id: ', self.current_frame_id)
61 |
62 | def update_to_specific_video_moment(self) -> None:
63 | """Iterates to a specifc frame and updates the video parameters"""
64 | self.set_current_frame_position_by_index()
65 | self.decode_current_frame()
66 | self.update_current_frame_id()
67 | self.update_video_time()
68 |
69 | def get_milliseconds(self) -> int:
70 | return (self.current_frame_id / self.fps) * 1000
71 |
72 | def get_seconds(self) -> float:
73 | return self.current_frame_id / self.fps
74 |
75 | def get_current_frame_id(self) -> int:
76 | return int(self.video.get(cv2.CAP_PROP_POS_FRAMES))
77 |
78 | def get_total_number_of_frames(self) -> int:
79 | return self.video.get(cv2.CAP_PROP_FRAME_COUNT)
80 |
81 | def get_video_fps(self) -> float:
82 | return self.video.get(cv2.CAP_PROP_FPS)
83 |
84 | def get_time_between_frames(self) -> float:
85 | return calculate_time_beetwen_frames(self.fps)
86 |
87 | def close_video(self) -> None:
88 | self.video.release()
--------------------------------------------------------------------------------
/cvplayer/core/media_player/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/__init__.py
--------------------------------------------------------------------------------
/cvplayer/core/media_player/default.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/default.mp4
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/back_end/image_player.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import os
3 | import numpy as np
4 | class ImagePlayer():
5 | def __init__(self, image_viewer, custom_method) -> None:
6 | #it eill start the viewer, and then loads an default image on it
7 | #after that you will be able to change the image
8 | self.image_viewer = image_viewer
9 | self.custom_method = custom_method
10 | self.image = None
11 |
12 | def set_image(self, image_path):
13 | if os.path.splitext(image_path)[1] == '.npy':
14 | self.image = np.load(image_path)
15 | else:
16 | self.image = cv2.imread(image_path)
17 | self.image = self.check_image(self.image)
18 | self.image_viewer.show_cv2_image(self.custom_method(self.image))
19 |
20 | def check_image(self, image):
21 | w, h, c = image.shape
22 | if image is None or w == 0 or h == 0 or c == 0:
23 | raise ValueError("Image is not valid")
24 | if c > 3:
25 | image = image[:, :, :3]
26 | image = image.astype(np.uint8)
27 | print("Image has more than 3 channels, it will be converted to 3 channels")
28 | return image
29 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/__init__.py:
--------------------------------------------------------------------------------
1 | from .add_images_buttons import AddImagesButton
2 | from .images_counter import ImageCounter
3 | from .image_viewer import ImageViewer
4 | from .images_list import ImagesList
5 | from .media_buttons import NextImageButton, PreviousImageButton
6 |
7 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/add_images_buttons.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QPushButton, QFileDialog
2 | from cvplayer.core.utils.widgets_utils import start_widget_basics
3 | from pkg_resources import resource_filename
4 |
5 | class AddImagesButton(QPushButton):
6 | def __init__(self, images_list, layout=None):
7 | super().__init__()
8 | start_widget_basics(self, layout)
9 | self.set_css()
10 | self.images_list = images_list
11 | self.clicked.connect(self.get_images)
12 |
13 | def get_images(self):
14 | images_files = QFileDialog.getOpenFileNames(self, "Select Images", "", "Image Files (*.jpg *.png *.jpeg *.JPG *.PNG *.JPEG *.tif *.tiff *.npy)")[0]
15 | if len(images_files) > 0:
16 | self.images_list.add_images(images_files)
17 |
18 | def set_css(self):
19 | add_icon_path = resource_filename(__name__, 'icons/add.png').replace("\\", "/")
20 | hover_icon_path = resource_filename(__name__, 'icons/add_hover.png').replace("\\", "/")
21 | pressed_icon_path = resource_filename(__name__, 'icons/add_pressed.png').replace("\\", "/")
22 | css_str = f"""
23 | QPushButton {{
24 | background-color: transparent;
25 | border-radius: 10px;
26 | border-image: url({add_icon_path});
27 | max-width: 50px;
28 | max-height: 50px;
29 | min-width: 50px;
30 | min-height: 50px;
31 | margin: 0%;
32 | padding: 0%;
33 | border: 0px;
34 | }}
35 | QPushButton:hover {{
36 | border-image: url({hover_icon_path});
37 | }}
38 | QPushButton:pressed {{
39 | border-image: url({pressed_icon_path});
40 | }}
41 | """
42 | self.setStyleSheet(css_str)
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/ScreenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/ScreenShot.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/White.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/add.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/add_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/add_hover.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/add_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/add_pressed.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/black.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/cvplayerlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/cvplayerlogo.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/drop_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/drop_down.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/next.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/next2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/next2.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/pause.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/play.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/previous.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/previous2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/previous2.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/icons/solid-color-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/image_player_core/interface/icons/solid-color-image.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/image_viewer.py:
--------------------------------------------------------------------------------
1 | from PyQt6 import QtCore, QtGui, QtWidgets
2 | import cvplayer.core.utils.widgets_utils as widgets_utils
3 | import cvplayer.core.utils.video_utils as video_utils
4 | import cv2
5 | from pkg_resources import resource_filename
6 |
7 | class ImageViewer(QtWidgets.QGraphicsView):
8 | photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
9 | change_image_on_viewer = QtCore.pyqtSignal()
10 |
11 | def __init__(self, layout=None, CSS=None):
12 | super(ImageViewer, self).__init__()
13 | widgets_utils.start_widget_basics(self, layout, CSS)
14 | self.cv2_image = None
15 | self.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
16 | self._zoom = 0
17 | self._empty = True
18 | self._scene = QtWidgets.QGraphicsScene(self)
19 | self._photo = QtWidgets.QGraphicsPixmapItem()
20 | self._scene.addItem(self._photo)
21 | self.setScene(self._scene)
22 | self.setTransformationAnchor(QtWidgets.QGraphicsView.ViewportAnchor.AnchorUnderMouse)
23 | self.setResizeAnchor(QtWidgets.QGraphicsView.ViewportAnchor.AnchorUnderMouse)
24 | self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
25 | self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
26 | self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
27 | self.setFrameShape(QtWidgets.QFrame.Shape.NoFrame )
28 | self.change_image_on_viewer.connect(self.setPhoto)
29 | self.show_cv2_image(cv2.imread(resource_filename(__name__, 'icons/black.png')))
30 |
31 | def show_cv2_image(self, cv2_image):
32 | self.QPixmap = video_utils.cv2_image_to_QPixmap(cv2_image)
33 | self.change_image_on_viewer.emit()
34 |
35 | def hasPhoto(self):
36 | return not self._empty
37 |
38 | def fitInView(self, scale=True):
39 | rect = QtCore.QRectF(self._photo.pixmap().rect())
40 | self.setSceneRect(rect)
41 | unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
42 | self.scale(1 / unity.width(), 1 / unity.height())
43 | viewrect = self.viewport().rect()
44 | scenerect = self.transform().mapRect(rect)
45 | factor = min(viewrect.width() / scenerect.width(),
46 | viewrect.height() / scenerect.height())
47 | self.scale(factor, factor)
48 | self._zoom = 0
49 |
50 | def setPhoto(self):
51 | self._zoom = 0
52 | self._empty = False
53 | self.setDragMode(QtWidgets.QGraphicsView.DragMode.ScrollHandDrag)
54 | self._photo.setPixmap(self.QPixmap)
55 | self.fitInView()
56 |
57 | def wheelEvent(self, event):
58 | if self.hasPhoto():
59 | if event.angleDelta().y() > 0:
60 | factor = 1.25
61 | self._zoom += 1
62 | else:
63 | factor = 0.8
64 | self._zoom -= 1
65 | if self._zoom > 0:
66 | self.scale(factor, factor)
67 | elif self._zoom == 0:
68 | self.fitInView()
69 | else:
70 | self._zoom = 0
71 |
72 | def toggleDragMode(self):
73 | if self.dragMode() == QtWidgets.QGraphicsView.DragMode.ScrollHandDrag:
74 | self.setDragMode(QtWidgets.QGraphicsView.DragMode.NoDrag)
75 | elif not self._photo.pixmap().isNull():
76 | self.setDragMode(QtWidgets.QGraphicsView.DragMode.ScrollHandDrag)
77 |
78 | def mousePressEvent(self, event):
79 | if self._photo.isUnderMouse():
80 | self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
81 | super(ImageViewer, self).mousePressEvent(event)
82 |
83 |
84 |
85 | # class ImageViewer(QtWidgets.QLabel):
86 | # def __init__(self, layout=None, CSS='cvplayer/stylesheets/image_viewer.css'):
87 | # super().__init__()
88 | # self.setScaledContents(True)
89 | # widgets_utils.start_widget_basics(self, layout, CSS)
90 | # self.cv2_image = cv2.imread('/home/eduardo/Pictures/Screenshot from 2022-10-02 10-54-40.png')
91 | # self.show_cv2_image(self.cv2_image)
92 |
93 | # def show_cv2_image(self, cv2_image):
94 | # QPixmap = video_utils.cv2_image_to_QPixmap(cv2_image)
95 |
96 | # w= self.width()
97 | # h= self.height()
98 |
99 | # scaled_pixmap = QPixmap#.scaled(w,h, QtCore.Qt.AspectRatioMode.KeepAspectRatio)
100 |
101 | # self.setPixmap(scaled_pixmap)
102 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/images_counter.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QLabel
2 | from cvplayer.core.utils.widgets_utils import start_widget_basics
3 |
4 | class ImageCounter(QLabel):
5 | def __init__(self, images_list, layout, CSS = 'stylesheets/image_counter.css', X=40, Y=40):
6 | super().__init__()
7 | self.images_list = images_list
8 | start_widget_basics(self, layout, CSS)#, fixed_width=X,fixed_height=Y)
9 | self.set_image_counter()
10 | self.images_list.currentIndexChanged.connect(self.set_image_counter)
11 | self.images_list.image_added.connect(self.set_image_counter)
12 |
13 | def set_image_counter(self):
14 | self.setText(f'{self.images_list.currentIndex() + 1}/{self.images_list.count()}')
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/images_list.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QComboBox
2 | from PyQt6.QtCore import pyqtSignal, Qt
3 | from cvplayer.core.utils.widgets_utils import start_widget_basics
4 | import os
5 | from pkg_resources import resource_filename
6 |
7 | class ImagesList(QComboBox):
8 | images_dict = {}
9 | image_added = pyqtSignal()
10 | current_image_path = None
11 | image_player = None
12 |
13 | def __init__(self, image_player, layout,X=350,Y=40) -> None:
14 | super().__init__()
15 | start_widget_basics(self, layout, fixed_height=Y)
16 | self.set_css()
17 | self.image_player = image_player
18 | self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
19 | self.setMinimumWidth(X)
20 | self.currentIndexChanged.connect(self.set_current_image)
21 |
22 | def set_current_image(self, image_index):
23 | self.current_image_path = self.images_dict[self.currentText()]
24 | self.image_player.set_image(self.current_image_path)
25 |
26 | def add_images(self, images_path):
27 | for image_path in images_path:
28 | self.check_video_path(image_path)
29 | image_name = os.path.basename(image_path)
30 | self.images_dict[image_name] = image_path
31 | self.addItem(image_name)
32 | self.image_added.emit()
33 | #self.setCurrentIndex(-1)
34 |
35 | def check_video_path(self, video_path):
36 | if not os.path.exists(video_path):
37 | raise FileNotFoundError("Image path not found")
38 | elif os.path.isfile(video_path):
39 | return True
40 |
41 | def set_css(self):
42 | css_str = """
43 | QComboBox {
44 | border-radius: 7px;
45 | color: lightgray;
46 | background-color: #333333;
47 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
48 | min-width: 350px;
49 | max-width: 350px;
50 | }
51 | QComboBox::hover{
52 |
53 | background-color: #4e5050;
54 | border-radius: 5px;
55 | }
56 | QComboBox:!editable:on, QComboBox::drop-down:editable:on {
57 | background: #4e5050;
58 | }
59 | QComboBox::drop-down {
60 | subcontrol-origin: padding;
61 | subcontrol-position: top right;
62 | width: 30px;
63 | border-bottom-right-radius: 3px;
64 | border-radius: 5px;
65 | }
66 | QComboBox::down-arrow {
67 | image: url(""" + resource_filename(__name__, 'icons/drop_down.png').replace("\\", "/") + """);
68 | height: 15px;
69 | width: 15px;
70 | }
71 | QComboBox QAbstractItemView {
72 | border-radius: 5px;
73 | selection-color: #4e5050;
74 | selection-background-color: #4e5050;
75 | color: rgba(91,91,91,255);
76 | background-color: #4e5050;
77 | outline: 0px;
78 | font-size: 12pt;
79 | font-weight: 400;
80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
81 | }
82 | QListView::item {
83 | height:40px;
84 | outline: 0px;
85 | }
86 | QListView::item:selected {
87 | background-color: #4e5050;
88 | outline: 0px;
89 | }
90 | QScrollBar{
91 | width:0px;
92 | }"""
93 | self.setStyleSheet(css_str)
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_core/interface/media_buttons.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QPushButton
2 | import cvplayer.core.utils.widgets_utils as widgets_utils
3 | from PyQt6.QtGui import QKeySequence
4 | from pkg_resources import resource_filename
5 | class NextImageButton(QPushButton):
6 | def __init__(self, images_list, layout, X=40, Y=40):
7 | super().__init__()
8 | self.setShortcut(QKeySequence('Right'))
9 | widgets_utils.start_widget_basics(self, layout, fixed_width=X,fixed_height=Y)
10 | self.set_css()
11 | self.images_list = images_list
12 | self.pressed.connect(self.next_image)
13 |
14 | def next_image(self):
15 | if self.images_list.currentIndex() + 1 < self.images_list.count():
16 | self.images_list.setCurrentIndex(self.images_list.currentIndex() + 1)
17 |
18 | def set_css(self):
19 | css_str = """
20 | QPushButton{
21 | border-image: url("""+ resource_filename(__name__, 'icons/next2.png').replace("\\", "/") +""");
22 | background:transparent;
23 | border-radius: 10px;
24 | border: none;
25 | outline: none;
26 | }
27 | QPushButton:hover {
28 | background-color: rgba(206, 197, 197, 0.21);
29 | }
30 | QPushButton:pressed{
31 | background-color: rgba(132, 129, 129, 0.264);
32 | }
33 | """
34 | self.setStyleSheet(css_str)
35 |
36 | class PreviousImageButton(QPushButton):
37 | def __init__(self, images_list, layout, X=40, Y=40):
38 | super().__init__()
39 | self.setShortcut(QKeySequence('Left'))
40 | widgets_utils.start_widget_basics(self, layout, fixed_width=X,fixed_height=Y)
41 | self.set_css()
42 | self.images_list = images_list
43 | self.pressed.connect(self.previous_image)
44 |
45 | def previous_image(self):
46 | if self.images_list.currentIndex() >= 1:
47 | self.images_list.setCurrentIndex(self.images_list.currentIndex() - 1)
48 |
49 | def set_css(self):
50 | css_str = """
51 | QPushButton{
52 | border-image: url("""+ resource_filename(__name__, 'icons/previous2.png').replace("\\", "/") +""");
53 | background:transparent;
54 | border-radius: 10px;
55 | border: none;
56 | outline: none;
57 | }
58 | QPushButton:hover {
59 | background-color: rgba(206, 197, 197, 0.21);
60 | }
61 | QPushButton:pressed{
62 | background-color: rgba(132, 129, 129, 0.264);
63 | }
64 | """
65 | self.setStyleSheet(css_str)
66 |
67 |
68 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/image_player_widget.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy
2 | from PyQt6.QtCore import Qt
3 | from cvplayer.core.media_player.image_player_core.interface import *
4 | from cvplayer.core.media_player.image_player_core.back_end.image_player import ImagePlayer
5 | from cvplayer.core.utils.widgets_utils import start_widget_basics
6 | import cv2
7 |
8 | class ImagePlayerWidget(QWidget):
9 | def __init__(self, custom_class) -> None:
10 | super().__init__()
11 | start_widget_basics(self, None, 'stylesheets/image_player_widget.css', minimum_height=300, minimum_width=300)
12 | self.custom_class = custom_class
13 | self.image_viewer = ImageViewer()
14 | self.image_player = ImagePlayer(self.image_viewer, self.custom_class.custom_method)
15 | self.build_ui_elements()
16 |
17 | def build_ui_elements(self):
18 | self.main_layout = QVBoxLayout(self)
19 | self.top_layout = QHBoxLayout()
20 |
21 | self.main_layout.addLayout(self.top_layout)
22 | self.main_layout.addWidget(self.image_viewer)#, alignment= Qt.AlignmentFlag.AlignCenter)
23 |
24 | self.images_list = ImagesList(self.image_player, self.top_layout)
25 | self.top_layout.addSpacerItem(QSpacerItem(0,0, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
26 | self.previous_image_button = PreviousImageButton(self.images_list,self.top_layout)
27 | self.image_counter = ImageCounter(self.images_list,self.top_layout)
28 | self.next_image_button = NextImageButton(self.images_list,self.top_layout)
29 | self.top_layout.addSpacerItem(QSpacerItem(0,0, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
30 | self.add_videos_button = AddImagesButton(self.images_list, self.top_layout)
31 |
32 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/__init__.py
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/back_end/video_player.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import pyqtSignal, QObject
2 | from cvplayer.core.updater.video_updater import VideoUpdater
3 | from cvplayer.core.media_objects.video import Video
4 | import time
5 |
6 | class VideoPlayer(QObject):
7 | started = pyqtSignal()
8 | def __init__(self, video: Video = None, custom_class=None) -> None:
9 | super().__init__()
10 | self.video = video
11 | self.create_video_updater()
12 | self.current_time = time.time()
13 | self.custom_class = custom_class
14 |
15 | def play_video(self) -> None:
16 | self.video_updater.start_time_updater()
17 |
18 | def pause_video(self) -> None:
19 | self.video_updater.stop_time_updater()
20 |
21 | def change_frame(self, frame_id : int)-> None:
22 | if 0 <= frame_id < self.video.total_number_of_frames:
23 | self.video.set_current_frame_id(frame_id)
24 | self.video_updater.start_signal_updater()
25 |
26 | def change_frame_for_first_video(self)-> None:
27 | self.video.set_current_frame_id(0)
28 | self.video.update_to_specific_video_moment()
29 | self.show_frame()
30 | self.slider.set_range(self.video.total_number_of_frames)
31 | self.update_ui_elements()
32 |
33 | def change_speed(self, speed: int):
34 | """speed is a positive integer, 1 is normal speed, 2 is double speed, 3 is triple speed, etc."""
35 | self.video.set_frames_to_jump(speed)
36 |
37 | def show_frame(self):
38 | if self.video.current_frame is not None:
39 | self.viewer.show_cv2_image(self.video.current_frame)
40 |
41 | def add_ui_elements(self, viewer, time_counter, slider, play):
42 | self.viewer = viewer
43 | self.time_counter = time_counter
44 | self.slider = slider
45 | self.play = play
46 |
47 | def set_video(self, video: Video):
48 | self.video_updater.stop_time_updater()
49 | self.video = video
50 | self.create_video_updater()
51 | self.change_frame(0)
52 | self.slider.set_range(self.video.total_number_of_frames)
53 | self.update_ui_elements()
54 |
55 | def update_ui_elements_each_second(self):
56 | if self.current_time + 1 < time.time():
57 | self.current_time = time.time()
58 | self.slider.set_value(int(self.video.current_frame_id))
59 | self.time_counter.update_time(self.video.current_milliseconds)
60 |
61 | def update_ui_elements(self):
62 | self.slider.set_value(self.video.current_frame_id)
63 | self.time_counter.update_time(self.video.current_milliseconds)
64 | self.play.set_paused()
65 |
66 | def custom_method(self):
67 | self.video.current_frame = self.custom_class.custom_method(self.video.current_frame)
68 |
69 | def create_video_updater(self):
70 | self.video_updater = VideoUpdater(self.video.fps)
71 |
72 | self.video_updater.add_method_on_timer_updater('update_frame', self.video.update_to_next_video_moment)
73 | self.video_updater.add_method_on_timer_updater('update_viewer', self.custom_method)
74 | self.video_updater.add_method_on_timer_updater('update_viewe', self.show_frame)
75 | self.video_updater.add_method_on_timer_updater('update_ui_elements', self.update_ui_elements_each_second)
76 |
77 | self.video_updater.add_method_on_signal_updater('jump_to_frame', self.video.update_to_specific_video_moment)
78 | self.video_updater.add_method_on_signal_updater('update_viewer', self.custom_method)
79 | self.video_updater.add_method_on_signal_updater('update_viewe', self.show_frame)
80 | self.video_updater.add_method_on_signal_updater('update_ui_elements', self.update_ui_elements)
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/back_end/videos_manager.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QComboBox
2 | from utils import WidgetsUtils
3 | import os
4 |
5 | class VideosManager():
6 | def __init__(self, video_player_widget) -> None:
7 | self.video_player_widget = video_player_widget
8 | self.videos_dict = {}
9 | self.current_video = None
10 |
11 | self.populate_videos_dict(videos_path)
12 |
13 | def __init__(self, video_player, videos_path, layout, css_path='stylesheets/videos_list.css',X=30,Y=32) -> None:
14 | super().__init__()
15 | start_widget_basics(self, layout, css_path, fixed_height=Y)
16 | self.video_player = video_player
17 | self.setInsertPolicy(QComboBox.NoInsert)
18 | self.populate_video_dict(videos_path)
19 |
20 | def populate_videos_dict(self, videos_path):
21 | self.clear()
22 | self.check_video_path(videos_path)
23 |
24 | for video_path in self.videos_list:
25 | video_name = os.path.basename(video_path)
26 | self.addItem(video_name)
27 | self.videos_dict[video_name] = video_path
28 |
29 | def check_video_path(self, video_path):
30 | if not os.path.exists(video_path):
31 | raise FileNotFoundError("Video path not found")
32 | elif os.path.isdir(video_path):
33 | self.videos_list = self.find_all_mp4(video_path)
34 | elif os.path.isfile(video_path):
35 | self.videos_list = [video_path]
36 |
37 | def find_all_mp4(self, path):
38 | return glob.glob(os.path.join(path, '**', '*.mp4'), recursive=True)
39 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/__init__.py:
--------------------------------------------------------------------------------
1 | from cvplayer.core.media_player.video_player_core.interface.media_buttons import PlayPauseButton, PreviousFrameButton, NextFrameButton
2 | from cvplayer.core.media_player.video_player_core.interface.video_speed_button import VideoSpeedButton
3 | from cvplayer.core.media_player.video_player_core.interface.slider import VideoSlider
4 | from cvplayer.core.media_player.video_player_core.interface.viewer import ImageViewer
5 | from cvplayer.core.media_player.video_player_core.interface.print_screen_button import PrintScreenButton
6 | from cvplayer.core.media_player.video_player_core.interface.video_time_counter import TimeCounter
7 | from cvplayer.core.media_player.video_player_core.interface.videos_list import VideosList
8 | from cvplayer.core.media_player.video_player_core.interface.add_videos_button import AddVideosButton
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/add_videos_button.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QPushButton, QFileDialog
2 | from PyQt6.QtGui import QPixmap, QIcon
3 | from cvplayer.core.utils.widgets_utils import start_widget_basics
4 | from pkg_resources import resource_filename
5 |
6 | class AddVideosButton(QPushButton):
7 | def __init__(self, videos_list, layout=None):
8 | super().__init__()
9 | start_widget_basics(self, layout)
10 | self.set_css()
11 | self.videos_list = videos_list
12 | self.clicked.connect(self.get_videos)
13 |
14 | def get_videos(self):
15 | mp4_files = QFileDialog.getOpenFileNames(self, "Select videos", "", "Video Files (*.mp4 *.MP4 *.avi *.mov)")[0]
16 | if len(mp4_files) > 0:
17 | self.videos_list.add_videos(mp4_files)
18 |
19 | def set_css(self):
20 | add_icon_path = resource_filename(__name__, 'icons/add.png').replace('\\', '/')
21 | hover_icon_path = resource_filename(__name__, 'icons/add_hover.png').replace('\\', '/')
22 | pressed_icon_path = resource_filename(__name__, 'icons/add_pressed.png').replace('\\', '/')
23 | css_str = f"""
24 | QPushButton {{
25 | background-color: transparent;
26 | border-radius: 10px;
27 | border-image: url({add_icon_path});
28 | max-width: 50px;
29 | max-height: 50px;
30 | min-width: 50px;
31 | min-height: 50px;
32 | margin: 0%;
33 | padding: 0%;
34 | border: 0px;
35 | }}
36 | QPushButton:hover {{
37 | border-image: url({hover_icon_path});
38 | }}
39 | QPushButton:pressed {{
40 | border-image: url({pressed_icon_path});
41 | }}
42 | """
43 | self.setStyleSheet(css_str)
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/ScreenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/ScreenShot.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/White.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/add.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/add_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/add_hover.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/add_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/add_pressed.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/black.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/cvplayerlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/cvplayerlogo.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/drop_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/drop_down.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/next.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/next2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/next2.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/pause.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/play.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/previous.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/previous2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/previous2.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/icons/solid-color-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/media_player/video_player_core/interface/icons/solid-color-image.png
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/media_buttons.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QPushButton, QToolButton
2 | import cvplayer.core.utils.widgets_utils as widgets_utils
3 | import cvplayer.core.utils.stylesheet_utils as stylesheet_utils
4 | from PyQt6.QtGui import QKeySequence
5 | from PyQt6.QtGui import QIcon
6 | from pkg_resources import resource_filename
7 |
8 | class PlayPauseButton(QToolButton):
9 | def __init__(self, video_player, layout, CSS='stylesheets/play_button.css', X=40, Y=40):
10 | super().__init__()
11 | self.start_button()
12 | widgets_utils.start_widget_basics(self, layout, CSS, fixed_width=X,fixed_height=Y)
13 | self.video_player = video_player
14 | self.pressed.connect(self.toggle)
15 | self.video_player.started.connect(self.unlock)
16 |
17 | def toggle(self):
18 | if self.is_paused:
19 | self.is_paused = False
20 | self.setIcon(QIcon(resource_filename(__name__, 'icons/pause.png')))
21 | self.video_player.play_video()
22 | else:
23 | self.is_paused = True
24 | self.setIcon(QIcon(resource_filename(__name__, 'icons/play.png')))
25 | self.video_player.pause_video()
26 |
27 | def set_paused(self):
28 | self.setIcon(QIcon(resource_filename(__name__, 'icons/play.png')))
29 | self.is_paused = True
30 |
31 | def start_button(self):
32 | self.setShortcut(QKeySequence('Space'))
33 | self.setAutoRaise(True)
34 | self.setIcon(QIcon(resource_filename(__name__, 'icons/play.png')))
35 | self.setIconSize(self.size())
36 | self.is_paused = True
37 | self.setEnabled(False)
38 |
39 | def unlock(self):
40 | self.setEnabled(True)
41 |
42 | class NextFrameButton(QPushButton):
43 | def __init__(self, video_player, layout, X=40, Y=40):
44 | super().__init__()
45 | self.setShortcut(QKeySequence('Right'))
46 | widgets_utils.start_widget_basics(self, layout, fixed_width=X,fixed_height=Y)
47 | self.set_css()
48 | self.setEnabled(False)
49 | self.video_player = video_player
50 | self.pressed.connect(self.next_frame)
51 | self.video_player.started.connect(self.unlock)
52 |
53 | def next_frame(self):
54 | next_video_frame = self.video_player.video.current_frame_id + 1
55 | self.video_player.change_frame(next_video_frame)
56 |
57 | def unlock(self):
58 | self.setEnabled(True)
59 |
60 | def set_css(self):
61 | image_path = resource_filename(__name__, 'icons/next.png').replace("\\", "/")
62 | css_string = """
63 | QPushButton{
64 | border-image: url("""+image_path+""");
65 | background:transparent;
66 | }
67 | QPushButton:hover {
68 | background-color: rgba(206, 197, 197, 0.21);
69 | }
70 | QPushButton:pressed{
71 | background-color: rgba(132, 129, 129, 0.264);
72 | } """
73 | self.setStyleSheet(css_string)
74 | class PreviousFrameButton(QPushButton):
75 | def __init__(self, video_player, layout, CSS = 'stylesheets/previous_frame_button.css',X=40, Y=40):
76 | super().__init__()
77 | self.setShortcut(QKeySequence('Left'))
78 | widgets_utils.start_widget_basics(self, layout, CSS, fixed_width=X,fixed_height=Y)
79 | self.set_css()
80 | self.video_player = video_player
81 | self.setEnabled(False)
82 | self.pressed.connect(self.previous_frame)
83 | self.video_player.started.connect(self.unlock)
84 |
85 | def previous_frame(self):
86 | previous_video_frame = self.video_player.video.current_frame_id - 1
87 | self.video_player.change_frame(previous_video_frame)
88 |
89 | def unlock(self):
90 | self.setEnabled(True)
91 |
92 | def set_css(self):
93 | image_path = resource_filename(__name__, 'icons/previous.png').replace("\\", "/")
94 | css_string = """
95 | QPushButton{
96 | border-image: url("""+image_path+""");
97 | background:transparent;
98 | }
99 | QPushButton:hover {
100 | background-color: rgba(206, 197, 197, 0.21);
101 | }
102 | QPushButton:pressed{
103 | background-color: rgba(132, 129, 129, 0.264);
104 | } """
105 | self.setStyleSheet(css_string)
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/print_screen_button.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QPushButton
2 | from PIL import ImageQt
3 | import cvplayer.core.utils.widgets_utils as widgets_utils
4 |
5 | class PrintScreenButton(QPushButton):
6 | def __init__(self, widget_to_print, video_player, layout, CSS='stylesheets/print_screen_button.css', tool_tip='Captura de tela', X=30, Y=30) -> None:
7 | super().__init__()
8 | widgets_utils.start_widget_basics(self, layout, CSS, tool_tip, fixed_width=X, fixed_height=Y)
9 | self.video_player = video_player
10 | self.clicked.connect(self.print_screen)
11 | self.widget_to_print = widget_to_print
12 |
13 | def print_screen(self):
14 | QPixmap_frame = self.widget_to_print.grab()
15 | print_image = ImageQt.fromqpixmap(QPixmap_frame)
16 | print_image.save("")
17 |
18 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/slider.py:
--------------------------------------------------------------------------------
1 | # from PyQt6 import QtCore
2 | # from PyQt6 import QtWidgets
3 | # from PyQt6.QtCore import Qt
4 |
5 | # #Credits for eyllanesc, thanks bro, for real.
6 | # #His StackOverflow account -> https://stackoverflow.com/users/6622587/eyllanesc
7 |
8 | # class VideoSlider(QtWidgets.QSlider):
9 | # def __init__(self, video_player, layout ,CSS='opencvplayer/stylesheets/video_slider.css'):
10 | # super().__init__()
11 | # self.setOrientation(Qt.Horizontal)
12 | # widgets_utils.start_widget_basics(self, layout, CSS)
13 | # self.video_player = video_player
14 | # self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
15 | # self.change_slider_limits(0, self.video_player.video.total_number_of_frames)
16 |
17 | # def mouseReleaseEvent(self, event):
18 | # super(VideoSlider, self).mouseReleaseEvent(event)
19 | # if event.button() == QtCore.Qt.LeftButton:
20 | # slider_value = self.pixel_pos_to_range_value(event.pos())
21 | # self.setValue(slider_value)
22 | # self.video_player.change_frame(slider_value)
23 |
24 | # def set_value(self, value):
25 | # self.setValue(value)
26 |
27 | # def change_slider_limits(self, min_value, max_value):
28 | # self.setMinimum(int(min_value))
29 | # self.setMaximum(int(max_value))
30 |
31 | # def pixel_pos_to_range_value(self, pos):
32 | # opt = QtWidgets.QStyleOptionSlider()
33 | # self.initStyleOption(opt)
34 | # gr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderGroove, self)
35 | # sr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderHandle, self)
36 |
37 | # if self.orientation() == QtCore.Qt.Horizontal:
38 | # sliderLength = sr.width()
39 | # sliderMin = gr.x()
40 | # sliderMax = gr.right() - sliderLength + 1
41 | # else:
42 | # sliderLength = sr.height()
43 | # sliderMin = gr.y()
44 | # sliderMax = gr.bottom() - sliderLength + 1;
45 | # pr = pos - sr.center() + sr.topLeft()
46 | # p = pr.x() if self.orientation() == QtCore.Qt.Horizontal else pr.y()
47 | # return QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), p - sliderMin,
48 | # sliderMax - sliderMin, opt.upsideDown)
49 |
50 |
51 |
52 |
53 |
54 | from PyQt6 import QtCore
55 | from PyQt6 import QtWidgets
56 | from PyQt6.QtWidgets import QStyle
57 | from PyQt6.QtCore import Qt
58 | import cvplayer.core.utils.widgets_utils as widgets_utils
59 |
60 |
61 | #Credits for eyllanesc, thanks bro, for real.
62 | #His StackOverflow account -> https://stackoverflow.com/users/6622587/eyllanesc
63 |
64 | class VideoSlider(QtWidgets.QSlider):
65 | def __init__(self, video_player, layout ,CSS='stylesheets/video_slider.css'):
66 | super().__init__()
67 | self.setOrientation(Qt.Orientation.Horizontal)
68 | widgets_utils.start_widget_basics(self, layout, CSS)
69 | self.video_player = video_player
70 | self.set_range(self.video_player.video.total_number_of_frames)
71 | self.setEnabled(False)
72 | self.video_player.started.connect(self.unlock)
73 |
74 | def mousePressEvent(self, e):
75 | if e.button() != Qt.MouseButton.LeftButton or not self.isEnabled():
76 | return super().mousePressEvent(self, e)
77 | e.accept()
78 | x = e.pos().x()
79 | value = (self.maximum() - self.minimum()) * x / self.width() + self.minimum()
80 | self.setValue(int(value))
81 | self.video_player.change_frame(value)
82 |
83 | # def mouseReleaseEvent(self, event):
84 | # super(VideoSlider, self).mouseReleaseEvent(event)
85 | # if event.button() == QtCore.Qt.MouseButton.LeftButton:
86 | # slider_value = self.pixel_pos_to_range_value(event.pos())
87 | # self.video_player.set_video_time(slider_value)
88 |
89 | def set_range(self, duration):
90 | self.setMinimum(0)
91 | self.setMaximum(int(duration))
92 |
93 | def set_value(self, value):
94 | self.setValue(int(value))
95 |
96 | def unlock(self):
97 | self.setEnabled(True)
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/video_speed_button.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import Qt
2 | from PyQt6.QtWidgets import QSpinBox
3 | from PyQt6.QtGui import QShortcut
4 | import cvplayer.core.utils.widgets_utils as widgets_utils
5 | from PyQt6.QtGui import QKeySequence
6 |
7 | class VideoSpeedButton(QSpinBox):
8 | def __init__(self, video_player, layout, CSS='stylesheets/video_speed_button.css'):
9 | super().__init__()
10 | widgets_utils.start_widget_basics(self, layout, CSS, tool_tip='Change video speed by pressing up and down arrows')
11 | self.setSuffix(' x')
12 | self.valueChanged.connect(video_player.change_speed)
13 | self.setValue(1)
14 | self.setReadOnly(True)
15 | self.start_shortcuts()
16 |
17 |
18 | def increase_speed(self):
19 | self.setValue(self.value() + 1)
20 |
21 | def decrease_speed(self):
22 | if self.value() > 1:
23 | self.setValue(self.value() - 1)
24 |
25 | def start_shortcuts(self):
26 | self.increase_speed_shortcut = QShortcut(QKeySequence('Up'), self)
27 | self.increase_speed_shortcut.activated.connect(self.increase_speed)
28 | self.decrease_speed_shortcut = QShortcut(QKeySequence('Down'), self)
29 | self.decrease_speed_shortcut.activated.connect(self.decrease_speed)
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/video_time_counter.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QLabel
2 | from cvplayer.core.utils.time_and_date_utils import milliseconds_to_counter_time
3 | from cvplayer.core.utils.widgets_utils import start_widget_basics
4 |
5 | class TimeCounter(QLabel):
6 | def __init__(self, video_player, layout, CSS='stylesheets/video_time_counter.css'):
7 | super().__init__()
8 | self.video_player = video_player
9 | start_widget_basics(self, layout, CSS)
10 | self.setText("00:00:00")
11 | self.setScaledContents(True)
12 | #self.setStyleSheet("background-color: rgb(200,200,200); font-size: 20px; color: black;")
13 |
14 | #layout.addWidget(self)
15 |
16 | def update_time(self, time_in_miliseconds):
17 | self.setText(milliseconds_to_counter_time(time_in_miliseconds))
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/videos_list.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QComboBox
2 | from cvplayer.core.utils.widgets_utils import start_widget_basics
3 | from cvplayer.core.media_objects.video import Video
4 | import os
5 | from pkg_resources import resource_filename
6 |
7 | class VideosList(QComboBox):
8 | videos_dict = {}
9 | current_video_path = None
10 | video_player = None
11 | started = False
12 | def __init__(self, video_player, layout,X=350,Y=40) -> None:
13 | super().__init__()
14 | start_widget_basics(self, layout, fixed_height=Y)
15 | self.set_css()
16 | self.video_player = video_player
17 | self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
18 | self.setMinimumWidth(X)
19 | self.currentIndexChanged.connect(self.change_video)
20 |
21 | def change_video(self, video_index):
22 | self.current_video_path = self.videos_dict[self.currentText()]
23 | self.video_player.set_video(Video(self.current_video_path))
24 | self.video_player.started.emit()
25 |
26 | def add_videos(self, videos_path):
27 | for video_path in videos_path:
28 | self.check_video_path(video_path)
29 | video_name = os.path.basename(video_path)
30 | self.videos_dict[video_name] = video_path
31 | self.addItem(video_name)
32 |
33 | def check_video_path(self, video_path):
34 | if not os.path.exists(video_path):
35 | raise FileNotFoundError("Video path not found")
36 | elif os.path.isfile(video_path):
37 | self.videos_list = [video_path]
38 |
39 | def set_css(self):
40 | image_path = resource_filename(__name__, 'icons/drop_down.png').replace("\\", "/")
41 | css_str = """
42 | QComboBox {
43 | border-radius: 7px;
44 | color: lightgray;
45 | background-color: #333333;
46 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
47 | min-width: 350px;
48 | max-width: 350px;
49 | }
50 | QComboBox::hover{
51 |
52 | background-color: #4e5050;
53 | border-radius: 5px;
54 | }
55 | QComboBox:!editable:on, QComboBox::drop-down:editable:on {
56 | background: #4e5050;
57 | }
58 | QComboBox::drop-down {
59 | subcontrol-origin: padding;
60 | subcontrol-position: top right;
61 | width: 30px;
62 | border-bottom-right-radius: 3px;
63 | border-radius: 5px;
64 | }
65 | QComboBox::down-arrow {
66 | image: url(""" + image_path + """);
67 | height: 15px;
68 | width: 15px;
69 | }
70 | QComboBox QAbstractItemView {
71 | border-radius: 5px;
72 | selection-color: #4e5050;
73 | selection-background-color: #4e5050;
74 | color: rgba(91,91,91,255);
75 | background-color: #4e5050;
76 | outline: 0px;
77 | font-size: 12pt;
78 | font-weight: 400;
79 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
80 | }
81 | QListView::item {
82 | height:40px;
83 | outline: 0px;
84 | }
85 | QListView::item:selected {
86 | background-color: #4e5050;
87 | outline: 0px;
88 | }
89 | QScrollBar{
90 | width:0px;
91 | }"""
92 | self.setStyleSheet(css_str)
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_core/interface/viewer.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QLabel
2 | import cvplayer.core.utils.widgets_utils as widgets_utils
3 | import cvplayer.core.utils.video_utils as video_utils
4 |
5 |
6 | class ImageViewer(QLabel):
7 | def __init__(self, layout, CSS='stylesheets/image_viewer.css'):
8 | super().__init__()
9 | self.setScaledContents(True)
10 | widgets_utils.start_widget_basics(self, layout, CSS)
11 | self.cv2_image = None
12 |
13 | def show_cv2_image(self, cv2_image):
14 | QPixmap = video_utils.cv2_image_to_QPixmap(cv2_image)
15 | self.setPixmap(QPixmap)
16 |
17 | # from PyQt5 import QtCore, QtGui, QtWidgets
18 | # import time
19 |
20 | # class ImageViewer(QtWidgets.QGraphicsView):
21 | # photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
22 | # change_image_on_viewer = QtCore.pyqtSignal()
23 |
24 | # def __init__(self, layout, CSS=None):
25 | # super(ImageViewer, self).__init__()
26 | # widgets_utils.start_widget_basics(self, layout, CSS)
27 | # self.cv2_image = None
28 |
29 | # self._zoom = 0
30 | # self._empty = True
31 | # self._scene = QtWidgets.QGraphicsScene(self)
32 | # self._photo = QtWidgets.QGraphicsPixmapItem()
33 | # self._scene.addItem(self._photo)
34 | # self.setScene(self._scene)
35 | # self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
36 | # self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
37 | # self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
38 | # self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
39 | # self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
40 | # self.setFrameShape(QtWidgets.QFrame.NoFrame)
41 | # self.change_image_on_viewer.connect(self.setPhoto)
42 |
43 | # def show_cv2_image(self, cv2_image):
44 | # self.QPixmap = video_utils.cv2_image_to_QPixmap(cv2_image)
45 | # self.change_image_on_viewer.emit()
46 |
47 | # def hasPhoto(self):
48 | # return not self._empty
49 |
50 | # def fitInView(self, scale=True):
51 | # rect = QtCore.QRectF(self._photo.pixmap().rect())
52 | # self.setSceneRect(rect)
53 | # unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
54 | # self.scale(1 / unity.width(), 1 / unity.height())
55 | # viewrect = self.viewport().rect()
56 | # scenerect = self.transform().mapRect(rect)
57 | # factor = min(viewrect.width() / scenerect.width(),
58 | # viewrect.height() / scenerect.height())
59 | # self.scale(factor, factor)
60 | # self._zoom = 0
61 |
62 | # def setPhoto(self):
63 | # start = time.time()
64 | # self._zoom = 0
65 | # self._empty = False
66 | # self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
67 | # self._photo.setPixmap(self.QPixmap)
68 |
69 | # self.fitInView()
70 | # end = time.time()
71 | # print("Time to set photo: ", end - start)
72 |
73 | # def wheelEvent(self, event):
74 | # if self.hasPhoto():
75 | # if event.angleDelta().y() > 0:
76 | # factor = 1.25
77 | # self._zoom += 1
78 | # else:
79 | # factor = 0.8
80 | # self._zoom -= 1
81 | # if self._zoom > 0:
82 | # self.scale(factor, factor)
83 | # elif self._zoom == 0:
84 | # self.fitInView()
85 | # else:
86 | # self._zoom = 0
87 |
88 | # def toggleDragMode(self):
89 | # if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
90 | # self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
91 | # elif not self._photo.pixmap().isNull():
92 | # self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
93 |
94 | # def mousePressEvent(self, event):
95 | # if self._photo.isUnderMouse():
96 | # self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
97 | # super(ImageViewer, self).mousePressEvent(event)
98 |
99 |
100 |
--------------------------------------------------------------------------------
/cvplayer/core/media_player/video_player_widget.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QSpacerItem, QSizePolicy
2 | from cvplayer.core.media_player.video_player_core.interface import *
3 | from cvplayer.core.media_objects.video import Video
4 | from cvplayer.core.media_player.video_player_core.back_end.video_player import VideoPlayer
5 | from cvplayer.core.utils.widgets_utils import start_widget_basics
6 | import os
7 | from pkg_resources import resource_filename
8 |
9 | class VideoPlayerWidget(QWidget):
10 | def __init__(self, custom_class) -> None:
11 | super().__init__()
12 | start_widget_basics(self, None, 'stylesheets/video_player_widget.css', minimum_height=300, minimum_width=300)
13 | self.video = Video(resource_filename(__name__, 'default.mp4'))
14 | self.custom_class = custom_class
15 | self.video_player = VideoPlayer(self.video, self.custom_class)
16 | self.build_ui_elements()
17 | self.video_player.change_frame_for_first_video()
18 |
19 | def build_ui_elements(self):
20 | self.main_layout = QVBoxLayout(self)
21 | self.top_layout = QHBoxLayout()
22 | self.bottom_layout = QHBoxLayout()
23 |
24 | self.main_layout.addLayout(self.top_layout)
25 | self.videos_list = VideosList(self.video_player, self.top_layout)
26 | spacer = QSpacerItem(1, 10, hPolicy= QSizePolicy.Policy.Expanding)
27 | self.top_layout.addItem(spacer)
28 | self.add_videos_button = AddVideosButton(self.videos_list, self.top_layout)
29 |
30 | self.image_viewer = ImageViewer(self.main_layout)
31 | self.main_layout.addLayout(self.bottom_layout)
32 |
33 | self.previous_frame_button = PreviousFrameButton(self.video_player,self.bottom_layout)
34 | self.play_pause_button = PlayPauseButton(self.video_player,self.bottom_layout)
35 | self.next_frame_button = NextFrameButton(self.video_player,self.bottom_layout)
36 | self.video_slider = VideoSlider(self.video_player,self.bottom_layout)
37 | self.time_counter = TimeCounter(self.video_player,self.bottom_layout)
38 | self.video_speed_button = VideoSpeedButton(self.video_player,self.bottom_layout)
39 |
40 | self.video_player.add_ui_elements(self.image_viewer, self.time_counter, self.video_slider, self.play_pause_button)
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/cvplayer/core/references.txt:
--------------------------------------------------------------------------------
1 | Icons used:
2 |
3 | Reproduzir icon by Icons8
4 | Retroceder icon by Icons8
5 | Avanço rápido icon by Icons8
6 | Seta para expandir icon by Icons8
7 | Mais 2 matemática icon by Icons8
8 | Mais 2 matemática icon by Icons8
9 | Avançar icon by Icons8
10 | Avançar icon by Icons8
11 |
--------------------------------------------------------------------------------
/cvplayer/core/updater/hybrid_updater.py:
--------------------------------------------------------------------------------
1 | from .time_updater import TimeUpdater
2 | import time
3 | from typing import NewType
4 |
5 | method = NewType('method', int)
6 | positive_int = NewType('positive_int', int)
7 |
8 | class HybridUpdater(TimeUpdater):
9 | __time_updater_boolean = False
10 | __signal_updater_boolean = False
11 | __time_updater_methods_dict = {}
12 | __signal_updater_methods_dict = {}
13 |
14 | def __init__(self, time_interval: positive_int = 1) -> None:
15 | super().__init__(time_interval)
16 |
17 | def start_time_updater(self) -> None:
18 | self.__time_updater_boolean = True
19 | self.__signal_updater_boolean = False
20 | self.quit()
21 | self.start()
22 |
23 | def stop_time_updater(self) -> None:
24 | self.__time_updater_boolean = False
25 | self.quit()
26 |
27 | def start_signal_updater(self) -> None:
28 | self.quit()
29 | self.__time_updater_boolean = False
30 | self.__signal_updater_boolean = True
31 | self.start()
32 |
33 | def __run_time_updater(self) -> None:
34 | while self.__time_updater_boolean:
35 | for method in self.__time_updater_methods_dict.values():
36 | method()
37 | time.sleep(self.time_interval)
38 |
39 | def __run_signal_updater(self) -> None:
40 | [method() for method in self.__signal_updater_methods_dict.values()]
41 | self.quit()
42 |
43 | def run(self) -> None:
44 | if self.__time_updater_boolean:
45 | self.__run_time_updater()
46 | elif self.__signal_updater_boolean:
47 | self.__run_signal_updater()
48 |
49 | def add_method_on_timer_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
50 | self.__time_updater_methods_dict[methodKey] = method_whitout_parameters
51 |
52 | def add_method_on_signal_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
53 | self.__signal_updater_methods_dict[methodKey] = method_whitout_parameters
54 |
55 | def remove_method_from_time_updater(self, method_key: str) -> str:
56 | return self.__time_updater_methods_dict.pop(method_key)
57 |
58 | def remove_method_from_signal_updater(self, method_key: str) -> str:
59 | return self.__signal_updater_methods_dict.pop(method_key)
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/cvplayer/core/updater/old_video_updater.py:
--------------------------------------------------------------------------------
1 | from .time_updater import TimeUpdater
2 | import time
3 | from typing import NewType
4 |
5 | method = NewType('method', int)
6 | positive_int = NewType('positive_int', int)
7 |
8 | class VideoUpdater(TimeUpdater):
9 | __time_updater_boolean = False
10 | __signal_updater_boolean = False
11 |
12 | def __init__(self, video_fps: float = 29.97, time_interval: positive_int = 0) -> None:
13 | super().__init__(time_interval)
14 | self.time_beetwen_frames = 1 / video_fps
15 | self.__time_updater_methods_dict = {}
16 | self.__signal_updater_methods_dict = {}
17 |
18 | def start_time_updater(self) -> None:
19 | self.quit()
20 | self.__time_updater_boolean = True
21 | self.__signal_updater_boolean = False
22 | self.start()
23 |
24 | def stop_time_updater(self) -> None:
25 | now = time.time()
26 | self.quit()
27 |
28 | self.__time_updater_boolean = False
29 | ####Extremamente perigoso!, concertar isso
30 |
31 | while self.isRunning() == True:## and now + 0.2 > time.time():
32 | continue
33 |
34 | def start_signal_updater(self) -> None:
35 | self.stop_time_updater()
36 | self.__time_updater_boolean = False
37 | self.__signal_updater_boolean = True
38 | self.start()
39 |
40 | def __run_time_updater(self) -> None:
41 | while self.__time_updater_boolean:
42 | start = time.time()
43 | for method in self.__time_updater_methods_dict.values():
44 | method()
45 | time.sleep(max(0, self.time_beetwen_frames - (time.time() - start)))
46 |
47 | def __run_signal_updater(self) -> None:
48 | [method() for method in self.__signal_updater_methods_dict.values()]
49 | self.quit()
50 |
51 | def run(self) -> None:
52 | if self.__time_updater_boolean:
53 | self.__run_time_updater()
54 | elif self.__signal_updater_boolean:
55 | self.__run_signal_updater()
56 |
57 | def add_method_on_timer_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
58 | self.__time_updater_methods_dict[methodKey] = method_whitout_parameters
59 |
60 | def add_method_on_signal_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
61 | self.__signal_updater_methods_dict[methodKey] = method_whitout_parameters
62 |
63 | def remove_method_from_time_updater(self, method_key: str) -> str:
64 | return self.__time_updater_methods_dict.pop(method_key)
65 |
66 | def remove_method_from_signal_updater(self, method_key: str) -> str:
67 | return self.__signal_updater_methods_dict.pop(method_key)
68 |
69 | def is_time_updater_running(self) -> bool:
70 | return self.__time_updater_boolean
71 |
72 |
73 |
74 | # qthread.quit(); // Cause the thread to cease.
75 | # qthread.wait();
76 |
77 |
78 | # myThread->m_abort = true; //Tell the thread to abort
79 | # if(!myThread->wait(5000)) //Wait until it actually has terminated (max. 5 sec)
80 | # {
81 | # myThread->terminate(); //Thread didn't exit in time, probably deadlocked, terminate it!
82 | # myThread->wait(); //We have to wait again here!
83 | # }
--------------------------------------------------------------------------------
/cvplayer/core/updater/time_updater.py:
--------------------------------------------------------------------------------
1 | import time
2 | from typing import NewType
3 | from .updater import Updater
4 |
5 | positive_int = NewType('positive_int', int)
6 |
7 | class TimeUpdater(Updater):
8 | run_updater = False
9 |
10 | def __init__(self, time_interval: positive_int = 1) -> None:
11 | super().__init__()
12 | """Executes the methods in a loop with a time interval in seconds."""
13 | self.time_interval = time_interval
14 |
15 | def run(self)-> None:
16 | while self.run_updater:
17 | for method in self.__methods_dict.values():
18 | method()
19 | time.sleep(self.time_interval)
20 |
21 | def start_updater(self)-> None:
22 | self.run_updater = True
23 | self.start()
24 |
25 | def stop_updater(self)-> None:
26 | self.run_updater = False
27 | self.quit()
28 |
29 | def set_time_interval(self, time_interval: positive_int)-> None:
30 | self.time_interval = time_interval
31 |
32 |
--------------------------------------------------------------------------------
/cvplayer/core/updater/updater.py:
--------------------------------------------------------------------------------
1 | from typing import NewType
2 | from PyQt6.QtCore import QThread
3 |
4 | method = NewType('method', int)
5 |
6 | class Updater(QThread):
7 | __methods_dict = {}
8 |
9 | def __init__(self) -> None:
10 | super().__init__()
11 | """Executes the methods added to it when runed."""
12 |
13 | def run(self)-> None:
14 | [method() for method in self.__methods_dict.values()]
15 | self.quit()
16 |
17 | def add_method_on_timer_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
18 | self.__methods_dict[methodKey] = method_whitout_parameters
19 |
20 | def remove_method(self, method_key: str) -> str:
21 | return self.__methods_dict.pop(method_key)
22 |
23 | def start_updater(self)-> None:
24 | self.start()
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/cvplayer/core/updater/video_updater.py:
--------------------------------------------------------------------------------
1 | from .time_updater import TimeUpdater
2 | import time
3 | import queue
4 | from typing import NewType
5 |
6 | method = NewType('method', int)
7 | positive_int = NewType('positive_int', int)
8 |
9 | class VideoUpdater(TimeUpdater):
10 | __time_updater_boolean = False
11 | __signal_updater_boolean = False
12 | __running = False
13 |
14 | def __init__(self, video_fps: float = 29.97, time_interval: positive_int = 0) -> None:
15 | super().__init__(time_interval)
16 | self.tasks_queue = queue.Queue()
17 | self.time_beetwen_frames = 1 / video_fps
18 | self.__time_updater_methods_dict = {}
19 | self.__signal_updater_methods_dict = {}
20 |
21 | def add_task(self, task) -> None:
22 | if self.__running == False:
23 | self.tasks_queue.put(task)
24 | self.start()
25 | elif self.__running == True:
26 | self.tasks_queue.put(task)
27 |
28 | def start_signal_updater(self) -> None:
29 | self.__time_updater_boolean = False
30 | self.__signal_updater_boolean = True
31 | self.add_task( self.__signal_updater_methods_dict)
32 |
33 | def start_time_updater(self) -> None:
34 | self.__time_updater_boolean = True
35 | self.__signal_updater_boolean = False
36 | self.add_task( self.__time_updater_methods_dict)
37 |
38 | def stop_time_updater(self) -> None:
39 | self.__time_updater_boolean = False
40 |
41 | def run(self) -> None:
42 | self.__running = True
43 | while self.tasks_queue.empty() == False:
44 | task = self.tasks_queue.get()
45 | for task_method in task.values():
46 | task_method()
47 | if self.__time_updater_boolean:
48 | start = time.time()
49 | self.tasks_queue.put( self.__time_updater_methods_dict)
50 | time.sleep(max(0, self.time_beetwen_frames - (time.time() - start)))
51 | self.__running = False
52 |
53 | def add_method_on_timer_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
54 | self.__time_updater_methods_dict[methodKey] = method_whitout_parameters
55 |
56 | def add_method_on_signal_updater(self, methodKey: str, method_whitout_parameters: method) -> None:
57 | self.__signal_updater_methods_dict[methodKey] = method_whitout_parameters
58 |
59 | def remove_method_from_time_updater(self, method_key: str) -> str:
60 | return self.__time_updater_methods_dict.pop(method_key)
61 |
62 | def remove_method_from_signal_updater(self, method_key: str) -> str:
63 | return self.__signal_updater_methods_dict.pop(method_key)
64 |
65 | def is_time_updater_running(self) -> bool:
66 | return self.__time_updater_boolean
--------------------------------------------------------------------------------
/cvplayer/core/utils/json_utils.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | def save_json(dict, path):
4 | with open(path, 'w') as json_path:
5 | json.dump(dict, json_path)
6 |
7 | def load_json(json_path):
8 | with open(json_path, 'r') as json_file:
9 | json_data = json.load(json_file)
10 | return json_data
11 |
12 | def labelme_to_egetra(labelme_dict):
13 | return {'latitude': labelme_dict['latitude'], 'longitude': labelme_dict['longitude'], 'data': labelme_dict['tempo'], 'shapes': labelme_dict['shapes']}
14 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/layout_utils.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QVBoxLayout
2 | from PyQt6 import QtCore
3 |
4 | def add_widget_in_layout(widget,layout, alignment= QtCore.Qt.AlignmentFlag.AlignVCenter):
5 | layout.addWidget(widget)#, alignment= alignment)
6 |
7 | def add_layout_in_layout(layout_father, layout_son):
8 | layout_father.addLayout(layout_son)
9 |
10 | def add_spacer_in_layout(layout, spacer):
11 | layout.addSpacerItem(spacer)
12 | QVBoxLayout.addWidget
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheet_utils.py:
--------------------------------------------------------------------------------
1 | from pkg_resources import resource_filename
2 |
3 | def load_css(css_path):
4 | readed_file = open(resource_filename(__name__, css_path),"r")
5 | return readed_file.read()
6 |
7 | def set_style_sheet(widget, css_path):
8 | qcss = load_css(css_path)
9 | widget.setStyleSheet (qcss)
10 |
11 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/add_videos_button.css:
--------------------------------------------------------------------------------
1 | QPushButton {
2 | background-color: transparent;
3 | border-radius: 10px;
4 | border-image: url('cvplayer/icons/add.png');
5 | max-width: 50px;
6 | max-height: 50px;
7 | min-width: 50px;
8 | min-height: 50px;
9 | margin: 0%;
10 | padding: 0%;
11 | border: 0px;
12 | }
13 | QPushButton:hover {
14 | border-image: url('cvplayer/icons/add_hover.png');
15 | }
16 | QPushButton:pressed {
17 | border-image: url('cvplayer/icons/add_pressed.png');
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/image_counter.css:
--------------------------------------------------------------------------------
1 | QLabel{
2 | background-color:rgba(31,29,30,255);
3 | color: white;
4 | font-size: 20px;
5 | font-weight: bold;
6 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/image_player_widget.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/utils/stylesheets/image_player_widget.css
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/image_viewer.css:
--------------------------------------------------------------------------------
1 | QLabel {
2 | background-color: rgba(23,28,34,255);
3 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/next_frame_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/next.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QPushButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/next_image_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/next2.png');
3 | background:transparent;
4 | border-radius: 10px;
5 | border: none;
6 | outline: none;
7 | }
8 | QPushButton:hover {
9 | background-color: rgba(206, 197, 197, 0.21);
10 | }
11 | QPushButton:pressed{
12 | background-color: rgba(132, 129, 129, 0.264);
13 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/pause_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/pause.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.103);
7 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/play_button.css:
--------------------------------------------------------------------------------
1 | QToolButton{
2 | background:transparent;
3 | border: none
4 | }
5 | QToolButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QToolButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/previous_frame_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/previous.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QPushButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/previous_image_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/previous2.png');
3 | background:transparent;
4 | border-radius: 10px;
5 | border: none;
6 | outline: none;
7 | }
8 | QPushButton:hover {
9 | background-color: rgba(206, 197, 197, 0.21);
10 | }
11 | QPushButton:pressed{
12 | background-color: rgba(132, 129, 129, 0.264);
13 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/print_screen_button.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/utils/stylesheets/print_screen_button.css
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/video_player_widget.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/core/utils/stylesheets/video_player_widget.css
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/video_slider.css:
--------------------------------------------------------------------------------
1 | QSlider::sub-page:Horizontal {
2 | background-color: #99eef1; }
3 |
4 |
5 | QSlider::add-page:Horizontal {
6 | background-color: #333333; }
7 |
8 | QSlider::groove:Horizontal {
9 | background: transparent;
10 | height:4px; }
11 |
12 | QSlider::handle:Horizontal {
13 | width:10px;
14 | border-radius:5px;
15 | background:#898989;
16 | margin: -5px 0px -5px 0px; }
17 |
18 |
19 | QSlider {
20 | background-color: rgba(0,0,0,0%); }
21 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/video_speed_button.css:
--------------------------------------------------------------------------------
1 | QSpinBox {
2 | border-radius: 10px;
3 | background-color: rgb(31, 29, 30);
4 | color: white;
5 | font-size: 20px;
6 | font-weight: bold;
7 | padding: 5px 0px 5px 0px;
8 |
9 |
10 | }
11 |
12 | /* background color when line edit is selected */
13 |
14 | QSpinBox::up-button {
15 | width: 0px;
16 | }
17 |
18 | QSpinBox::down-button {
19 | width: 0px;
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/video_time_counter.css:
--------------------------------------------------------------------------------
1 | QLabel{
2 | background-color:rgba(31,29,30,255);
3 | color: white;
4 | font-size: 20px;
5 | }
--------------------------------------------------------------------------------
/cvplayer/core/utils/stylesheets/videos_list.css:
--------------------------------------------------------------------------------
1 | QComboBox {
2 | border-radius: 7px;
3 | color: lightgray;
4 | background-color: #333333;
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | min-width: 350px;
7 | max-width: 350px;
8 | }
9 | QComboBox::hover{
10 |
11 | background-color: #4e5050;
12 | border-radius: 5px;
13 | }
14 | QComboBox:!editable:on, QComboBox::drop-down:editable:on {
15 | background: #4e5050;
16 | }
17 | QComboBox::drop-down {
18 | subcontrol-origin: padding;
19 | subcontrol-position: top right;
20 | width: 30px;
21 | border-bottom-right-radius: 3px;
22 | border-radius: 5px;
23 | }
24 | QComboBox::down-arrow {
25 | image: url('cvplayer/icons/drop_down.png');
26 | height: 15px;
27 | width: 15px;
28 | }
29 | QComboBox QAbstractItemView {
30 | border-radius: 5px;
31 | selection-color: #4e5050;
32 | selection-background-color: #4e5050;
33 | color: rgba(91,91,91,255);
34 | background-color: #4e5050;
35 | outline: 0px;
36 | font-size: 12pt;
37 | font-weight: 400;
38 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
39 | }
40 | QListView::item {
41 | height:40px;
42 | outline: 0px;
43 | }
44 | QListView::item:selected {
45 | background-color: #4e5050;
46 | outline: 0px;
47 | }
48 | QScrollBar{
49 | width:0px;
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/cvplayer/core/utils/time_and_date_utils.py:
--------------------------------------------------------------------------------
1 | def milliseconds_to_counter_time(miliseconds_time):
2 | seconds=int((miliseconds_time/1000)%60)
3 | minutes=int((miliseconds_time/(1000*60))%60)
4 | hours=int((miliseconds_time/(1000*60*60))%24)
5 |
6 | return "%02d:%02d:%02d" % (hours, minutes, seconds)
--------------------------------------------------------------------------------
/cvplayer/core/utils/video_utils.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtGui import QPixmap, QImage
2 | import cv2
3 |
4 | def calculate_time_beetwen_frames(video_fps):
5 | return 1 / video_fps
6 |
7 | #temporariamente aqui
8 | def cv2_image_to_QPixmap(cv2_image):
9 | height, width, channel = cv2_image.shape
10 | bytes_per_line = 3 * width
11 | qt_image = QImage(cv2_image.data, width, height, bytes_per_line, QImage.Format.Format_BGR888)
12 |
13 | return QPixmap(qt_image)
14 |
15 | def get_video_thumbnail(road_video_path: str):
16 | video = cv2.VideoCapture(road_video_path)
17 | success, cv2_thumbnail = video.read()
18 | if success:
19 | return cv2_image_to_QPixmap(cv2_thumbnail)
20 | else:
21 | print("Error: Could not get thumbnail from video")
22 | video.release()
23 | cv2.destroyAllWindows()
--------------------------------------------------------------------------------
/cvplayer/core/utils/widgets_utils.py:
--------------------------------------------------------------------------------
1 | from cvplayer.core.utils import layout_utils, stylesheet_utils
2 | from PyQt6.QtWidgets import QWidget, QLayout
3 | from PyQt6.QtCore import Qt
4 |
5 |
6 | def start_widget_basics(widget: QWidget, layout: QLayout = None, css_path: str = None, tool_tip: str = None, minimum_width: int=None, minimum_height: int=None, fixed_width: int=None, fixed_height: int=None, alignment: Qt.AlignmentFlag = None):
7 | """Initialize widget CSS, Layout, ToolTip and Minimum Sizes"""
8 | if css_path!=None:
9 | stylesheet_utils.set_style_sheet(widget, css_path)
10 | if layout!=None:
11 | layout_utils.add_widget_in_layout(widget, layout)
12 | if tool_tip!=None:
13 | widget.setToolTip(tool_tip)
14 | if minimum_width!=None:
15 | widget.setMinimumWidth(minimum_width)
16 | if minimum_height!=None:
17 | widget.setMinimumHeight(minimum_height)
18 | if fixed_width!=None:
19 | widget.setFixedWidth(fixed_width)
20 | if fixed_height!=None:
21 | widget.setFixedHeight(fixed_height)
22 | if alignment!=None and layout!=None:
23 | layout.setAlignment(alignment)
24 |
--------------------------------------------------------------------------------
/cvplayer/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu
2 |
3 | RUN apt-get update
4 | RUN apt-get install -y python3
5 | RUN apt-get install -y python3-pip
6 | RUN apt-get install libxcb-xinerama0
7 |
8 | RUN apt-get install -y libsm6 libxext6 libxrender-dev
9 |
10 | RUN apt-get update
11 | RUN pip install numpy==1.24.2
12 |
13 | RUN apt-get update
14 | RUN pip install setuptools==65.6.3 wheel==0.38.4
15 |
16 | RUN pip3 install PyQt6==6.4.2 PyQt6-Qt6==6.4.2 PyQt6-sip==13.4.1
17 |
18 | RUN pip3 install opencv-python-headless
19 |
20 | RUN pip install pillow
21 |
22 | RUN apt-get update
23 | RUN apt install qt6-base-abi -y
24 |
25 | RUN apt-get update
26 | RUN apt install qt6-base-dev -y
27 |
28 | RUN apt-get update
29 | RUN apt-get install -y libgl1-mesa-glx
30 |
31 | RUN apt-get update
32 | RUN apt-get install -y git
33 |
34 | WORKDIR /home
35 | RUN git clone https://github.com/edu010101/opencvplayer
36 |
37 | WORKDIR /home/opencvplayer/opencvplayer
38 |
39 | RUN pip3 install -e .
40 |
41 | WORKDIR /home/opencvplayer
42 |
43 | ENV DEBIAN_FRONTEND=noninteractive
44 |
45 | RUN adduser --quiet --disabled-password qtuser && usermod -a -G audio qtuser
46 |
47 | ENV LIBGL_ALWAYS_INDIRECT=1
48 |
49 | COPY ./opencvplayer/test.py /home/opencvplayer/test.py
50 | COPY ./opencvplayer/cut.mp4 /home/opencvplayer/cut.avi
51 |
52 | ENTRYPOINT python3 test.py
53 |
54 | #ENTRYPOINT bash
55 |
--------------------------------------------------------------------------------
/cvplayer/docker/runing_docker.txt:
--------------------------------------------------------------------------------
1 | HOW TO RUN THE DOCKER
2 |
3 | paste the following command making changes on the nescessary places
4 |
5 | sudo docker run --rm -it -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -u qtuser containername
6 |
7 |
--------------------------------------------------------------------------------
/cvplayer/icons/ScreenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/ScreenShot.png
--------------------------------------------------------------------------------
/cvplayer/icons/White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/White.png
--------------------------------------------------------------------------------
/cvplayer/icons/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/add.png
--------------------------------------------------------------------------------
/cvplayer/icons/add_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/add_hover.png
--------------------------------------------------------------------------------
/cvplayer/icons/add_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/add_pressed.png
--------------------------------------------------------------------------------
/cvplayer/icons/black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/black.png
--------------------------------------------------------------------------------
/cvplayer/icons/cvplayerlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/cvplayerlogo.png
--------------------------------------------------------------------------------
/cvplayer/icons/drop_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/drop_down.png
--------------------------------------------------------------------------------
/cvplayer/icons/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example.png
--------------------------------------------------------------------------------
/cvplayer/icons/example2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example2.mp4
--------------------------------------------------------------------------------
/cvplayer/icons/example2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example2.png
--------------------------------------------------------------------------------
/cvplayer/icons/example3.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example3.mp4
--------------------------------------------------------------------------------
/cvplayer/icons/example3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example3.png
--------------------------------------------------------------------------------
/cvplayer/icons/example4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example4.png
--------------------------------------------------------------------------------
/cvplayer/icons/example5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/example5.png
--------------------------------------------------------------------------------
/cvplayer/icons/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/next.png
--------------------------------------------------------------------------------
/cvplayer/icons/next2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/next2.png
--------------------------------------------------------------------------------
/cvplayer/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/pause.png
--------------------------------------------------------------------------------
/cvplayer/icons/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/play.png
--------------------------------------------------------------------------------
/cvplayer/icons/previous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/previous.png
--------------------------------------------------------------------------------
/cvplayer/icons/previous2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/previous2.png
--------------------------------------------------------------------------------
/cvplayer/icons/solid-color-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/icons/solid-color-image.png
--------------------------------------------------------------------------------
/cvplayer/image_player.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QApplication, QMainWindow
2 | from cvplayer.core.media_player.image_player_widget import ImagePlayerWidget
3 | import sys
4 |
5 | class ImagePlayer():
6 | def __init__(self, custom_class) -> None:
7 | app = QApplication(sys.argv)
8 | if not hasattr(custom_class, 'custom_method') or not callable(getattr(custom_class, 'custom_method')):
9 | raise RuntimeError('custom_method not found in the class')
10 | image_player = ImagePlayerWidget(custom_class)
11 | window = QMainWindow()
12 | window.setStyleSheet('background-color: rgba(31,29,30,255);')
13 | window.setMinimumSize(550,400)
14 | window.setCentralWidget(image_player)
15 | window.show()
16 | sys.exit(app.exec())
17 |
18 |
--------------------------------------------------------------------------------
/cvplayer/stylesheets/add_videos_button.css:
--------------------------------------------------------------------------------
1 | QPushButton {
2 | background-color: transparent;
3 | border-radius: 10px;
4 | border-image: url('cvplayer/icons/add.png');
5 | max-width: 50px;
6 | max-height: 50px;
7 | min-width: 50px;
8 | min-height: 50px;
9 | margin: 0%;
10 | padding: 0%;
11 | border: 0px;
12 | }
13 | QPushButton:hover {
14 | border-image: url('cvplayer/icons/add_hover.png');
15 | }
16 | QPushButton:pressed {
17 | border-image: url('cvplayer/icons/add_pressed.png');
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/cvplayer/stylesheets/image_counter.css:
--------------------------------------------------------------------------------
1 | QLabel{
2 | background-color:rgba(31,29,30,255);
3 | color: white;
4 | font-size: 20px;
5 | font-weight: bold;
6 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/image_player_widget.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/stylesheets/image_player_widget.css
--------------------------------------------------------------------------------
/cvplayer/stylesheets/image_viewer.css:
--------------------------------------------------------------------------------
1 | QLabel {
2 | background-color: rgba(23,28,34,255);
3 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/next_frame_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/next.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QPushButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/next_image_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/next2.png');
3 | background:transparent;
4 | border-radius: 10px;
5 | border: none;
6 | outline: none;
7 | }
8 | QPushButton:hover {
9 | background-color: rgba(206, 197, 197, 0.21);
10 | }
11 | QPushButton:pressed{
12 | background-color: rgba(132, 129, 129, 0.264);
13 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/pause_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/pause.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.103);
7 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/play_button.css:
--------------------------------------------------------------------------------
1 | QToolButton{
2 | background:transparent;
3 | border: none
4 | }
5 | QToolButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QToolButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/previous_frame_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/previous.png');
3 | background:transparent;
4 | }
5 | QPushButton:hover {
6 | background-color: rgba(206, 197, 197, 0.21);
7 | }
8 | QPushButton:pressed{
9 | background-color: rgba(132, 129, 129, 0.264);
10 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/previous_image_button.css:
--------------------------------------------------------------------------------
1 | QPushButton{
2 | border-image: url('cvplayer/icons/previous2.png');
3 | background:transparent;
4 | border-radius: 10px;
5 | border: none;
6 | outline: none;
7 | }
8 | QPushButton:hover {
9 | background-color: rgba(206, 197, 197, 0.21);
10 | }
11 | QPushButton:pressed{
12 | background-color: rgba(132, 129, 129, 0.264);
13 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/print_screen_button.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/stylesheets/print_screen_button.css
--------------------------------------------------------------------------------
/cvplayer/stylesheets/video_player_widget.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edu010101/cvplayer/0c1f049175ace1d4255c04937a527f02f37f5883/cvplayer/stylesheets/video_player_widget.css
--------------------------------------------------------------------------------
/cvplayer/stylesheets/video_slider.css:
--------------------------------------------------------------------------------
1 | QSlider::sub-page:Horizontal {
2 | background-color: #99eef1; }
3 |
4 |
5 | QSlider::add-page:Horizontal {
6 | background-color: #333333; }
7 |
8 | QSlider::groove:Horizontal {
9 | background: transparent;
10 | height:4px; }
11 |
12 | QSlider::handle:Horizontal {
13 | width:10px;
14 | border-radius:5px;
15 | background:#898989;
16 | margin: -5px 0px -5px 0px; }
17 |
18 |
19 | QSlider {
20 | background-color: rgba(0,0,0,0%); }
21 |
--------------------------------------------------------------------------------
/cvplayer/stylesheets/video_speed_button.css:
--------------------------------------------------------------------------------
1 | QSpinBox {
2 | border-radius: 10px;
3 | background-color: rgb(31, 29, 30);
4 | color: white;
5 | font-size: 20px;
6 | font-weight: bold;
7 | padding: 5px 0px 5px 0px;
8 |
9 |
10 | }
11 |
12 | /* background color when line edit is selected */
13 |
14 | QSpinBox::up-button {
15 | width: 0px;
16 | }
17 |
18 | QSpinBox::down-button {
19 | width: 0px;
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/cvplayer/stylesheets/video_time_counter.css:
--------------------------------------------------------------------------------
1 | QLabel{
2 | background-color:rgba(31,29,30,255);
3 | color: white;
4 | font-size: 20px;
5 | }
--------------------------------------------------------------------------------
/cvplayer/stylesheets/videos_list.css:
--------------------------------------------------------------------------------
1 | QComboBox {
2 | border-radius: 7px;
3 | color: lightgray;
4 | background-color: #333333;
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | min-width: 350px;
7 | max-width: 350px;
8 | }
9 | QComboBox::hover{
10 |
11 | background-color: #4e5050;
12 | border-radius: 5px;
13 | }
14 | QComboBox:!editable:on, QComboBox::drop-down:editable:on {
15 | background: #4e5050;
16 | }
17 | QComboBox::drop-down {
18 | subcontrol-origin: padding;
19 | subcontrol-position: top right;
20 | width: 30px;
21 | border-bottom-right-radius: 3px;
22 | border-radius: 5px;
23 | }
24 | QComboBox::down-arrow {
25 | image: url('cvplayer/icons/drop_down.png');
26 | height: 15px;
27 | width: 15px;
28 | }
29 | QComboBox QAbstractItemView {
30 | border-radius: 5px;
31 | selection-color: #4e5050;
32 | selection-background-color: #4e5050;
33 | color: rgba(91,91,91,255);
34 | background-color: #4e5050;
35 | outline: 0px;
36 | font-size: 12pt;
37 | font-weight: 400;
38 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
39 | }
40 | QListView::item {
41 | height:40px;
42 | outline: 0px;
43 | }
44 | QListView::item:selected {
45 | background-color: #4e5050;
46 | outline: 0px;
47 | }
48 | QScrollBar{
49 | width:0px;
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/cvplayer/video_player.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtWidgets import QApplication, QMainWindow
2 | from cvplayer.core.media_player.video_player_widget import VideoPlayerWidget
3 | import sys
4 |
5 | class VideoPlayer():
6 | def __init__(self, custom_class) -> None:
7 | app = QApplication(sys.argv)
8 | if not hasattr(custom_class, 'custom_method') or not callable(getattr(custom_class, 'custom_method')):
9 | raise RuntimeError('custom_method not found in the class')
10 | video_player = VideoPlayerWidget(custom_class)
11 | window = QMainWindow()
12 | window.setStyleSheet('background-color: rgba(31,29,30,255);')
13 | window.setCentralWidget(video_player)
14 | window.setMinimumSize(550,400)
15 | window.show()
16 | sys.exit(app.exec())
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/demo_mmdetection.py:
--------------------------------------------------------------------------------
1 | from cvplayer import ImagePlayer
2 | from mmdet.apis import inference_detector, init_detector
3 |
4 | #mmdectection example
5 | class CustomBase():
6 | def __init__(self) -> None: #always load the model in the constructor
7 | model_config='your_path_to_config/faster_rcnn_r50_fpn_1x.py' #you can use any model from mmdetection
8 | model_weights='your_path_to_weights/epoch_50.pth'
9 | self.detection_model = init_detector(model_config, model_weights, device='cuda:0')
10 |
11 | def custom_method(self, numpy_image):
12 | detection_result = inference_detector(self.detection_model, numpy_image)
13 | return self.detection_model.show_result(numpy_image, detection_result, score_thr=0.7, show=False)
14 |
15 | ImagePlayer(CustomBase()) #pass the class to the ImagePlayer and start the player
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/demo_yolov8.py:
--------------------------------------------------------------------------------
1 | from cvplayer import VideoPlayer
2 | from ultralytics import YOLO
3 | import cv2
4 |
5 | # yolov8 example
6 | class CustomBase():
7 | def __init__(self) -> None: #always load the model in the constructor
8 | self.model = YOLO("yolov8s.pt") # load a pretrained model
9 |
10 | def custom_method(self, numpy_image): #method to be called on each frame and do whatever you want(ALLWAYS RECIEVES THE IMAGE AS A NUMPY ARRAY)
11 | results = self.model(numpy_image) # predict on an image
12 | for result in results:
13 | boxes = result.boxes # Boxes object for bbox outputs
14 | classes = result.names
15 | for box in boxes: #just draw the boxes with the class name
16 | cv2.rectangle(numpy_image, (int(box.xyxy[0][0]), int(box.xyxy[0][1])), (int(box.xyxy[0][2]), int(box.xyxy[0][3])), (0, 255, 0), 2)
17 | cv2.putText(numpy_image, classes[int(box.cls)], (int(box.xyxy[0][0]), int(box.xyxy[0][1])), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 4)
18 | return numpy_image #return the image with the changes (ALLWAYS RETURN THE IMAGE AS A NUMPY ARRAY WITH 3 CHANNELS)
19 |
20 | VideoPlayer(CustomBase()) #pass the class to the VideoPlayer and start the player
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyQt6==6.4.2
2 | PyQt6-Qt6==6.4.2
3 | PyQt6-sip==13.4.1
4 | opencv-python
5 | pillow
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(name='cvplayer',
4 | version='0.1',
5 | packages=['cvplayer'],
6 | )
--------------------------------------------------------------------------------
/tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "metadata": {},
7 | "source": [
8 | "# cvplayer tutorial\n",
9 | " \n",
10 | "\n",
11 | "In this tutorial we will learn how to use cvplayer to create a video and image player for your model with just a few lines of code.\n",
12 | "\n",
13 | "The repository is divided in 2 main parts:\n",
14 | "\n",
15 | "1. The VideoPlayer\n",
16 | "2. The ImagePlayer\n",
17 | "\n",
18 | "NOTE: Everthing you can do with the VideoPlayer you can do with the ImagePlayer, the only difference is on the declaration."
19 | ]
20 | },
21 | {
22 | "attachments": {},
23 | "cell_type": "markdown",
24 | "metadata": {},
25 | "source": [
26 | "## The VideoPlayer !\n",
27 | "1. Is a class that you can use to create a video player that will call your custom method on each frame of the video.\n",
28 | "2. The VideoPlayer can jump to a specific frame, pause, play, change the speed of the video and more.\n",
29 | "3. To use the VideoPlayer you need to create a class that implement the custom_method.\n",
30 | "4. The custom_method is the method that will be called on each frame of the video.\n",
31 | "5. The custom_method must receive a numpy array with 3 channels and return a numpy array with 3 channels.\n",
32 | "\n",
33 | "### Controls\n",
34 | "\n",
35 | "Besides the mouse controls, the VideoPlayer has some keyboard shortcuts:\n",
36 | "\n",
37 | "- Play and pause the video by pressing the space bar.\n",
38 | "- Jump to the next and previous frame by pressing the right and left arrow keys.\n",
39 | "- Change the speed of the video by pressing the up and down arrow keys.\n",
40 | "- Switch and add videos new videos using the videos list and the add videos button.\n",
41 | "\n",
42 | "\n",
43 | "Here is an example with a custom method that just write a random number on the image"
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "metadata": {},
50 | "outputs": [],
51 | "source": [
52 | "#import the VideoPlayer class\n",
53 | "from cvplayer import VideoPlayer\n",
54 | "import cv2 #import opencv to write on the image\n",
55 | "import random #import random to generate a random number\n",
56 | "\n",
57 | "class CustomBase(): \n",
58 | " def __init__(self) -> None: #in this case we don't need to initialize anything\n",
59 | " pass\n",
60 | " \n",
61 | " def custom_method(self, numpy_image): #method to be called on each frame and do whatever you want\n",
62 | " #NOTE: the name of the method MUST be custom_method\n",
63 | " #NOTE: the method MUST receive a numpy array with 3 channels and return a numpy array with 3 channels\n",
64 | "\n",
65 | " cv2.putText(numpy_image, str(random.randint(0,100)), (150, 150), cv2.FONT_HERSHEY_SIMPLEX, 5, (255, 0, 0), 10) #write a random number on the image\n",
66 | " \n",
67 | " return numpy_image #return the image with the changes\n",
68 | " \n",
69 | "VideoPlayer(CustomBase()) #pass the class to the VideoPlayer and start the player\n",
70 | "\n"
71 | ]
72 | },
73 | {
74 | "attachments": {},
75 | "cell_type": "markdown",
76 | "metadata": {},
77 | "source": [
78 | "## The ImagePlayer !\n",
79 | "1. Is a class that you can use to create a image player that will call your custom method on each image.\n",
80 | "2. The ImagePlayer can jump to a specific image, zoom in, zoom out, and more.\n",
81 | "3. To use the ImagePlayer you need to create a class that implement the custom_method.\n",
82 | "4. The custom_method is the method that will be called on each image.\n",
83 | "5. The custom_method must receive a numpy array with 3 channels and return a numpy array with 3 channels.\n",
84 | "6. The ImagePlayer supports the following image formats: .jpg, .png, .jpeg, .tif, .npy\n",
85 | "7. You can jump to the next and previous image by pressing the right and left arrow keys.\n",
86 | "\n",
87 | "NOTE: The ImagePlayer supports .npy with more than 3 channels, but it will only show the first 3 channels, and your custom method will only receive the first 3 channels.\n",
88 | "\n",
89 | "\n",
90 | "Here is an example with a custom method but now lets use an object detection model to detect objects on the image"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "metadata": {},
97 | "outputs": [],
98 | "source": [
99 | "from cvplayer import ImagePlayer #import the ImagePlayer class\n",
100 | "from ultralytics import YOLO #import the YOLO model\n",
101 | "import cv2 #import opencv to write on the image\n",
102 | "\n",
103 | "# yolov8 example\n",
104 | "class CustomBase(): \n",
105 | " def __init__(self) -> None:\n",
106 | " #NOTE: always initialize your model in the constructor\n",
107 | " self.model = YOLO(\"yolov8s.pt\") # load a pretrained model \n",
108 | " \n",
109 | " def custom_method(self, numpy_image): #method to be called on each frame and do whatever you want\n",
110 | " #NOTE: the name of the method MUST be custom_method\n",
111 | " #NOTE: the method MUST receive a numpy array with 3 channels and return a numpy array with 3 channels\n",
112 | "\n",
113 | " results = self.model(numpy_image) # predict on an image\n",
114 | " for result in results:\n",
115 | " boxes = result.boxes # Boxes object for bbox outputs\n",
116 | " classes = result.names\n",
117 | " for box in boxes: #loop through the boxes and draw them on the image with the class name\n",
118 | " cv2.rectangle(numpy_image, (int(box.xyxy[0][0]), int(box.xyxy[0][1])), (int(box.xyxy[0][2]), int(box.xyxy[0][3])), (0, 255, 0), 2)\n",
119 | " cv2.putText(numpy_image, classes[int(box.cls)], (int(box.xyxy[0][0]), int(box.xyxy[0][1])), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 4)\n",
120 | " \n",
121 | " return numpy_image #return the image with the changes (numpy array with 3 channels)\n",
122 | " \n",
123 | "ImagePlayer(CustomBase()) #pass the class to the ImagePlayer and start the player\n",
124 | "\n"
125 | ]
126 | }
127 | ],
128 | "metadata": {
129 | "kernelspec": {
130 | "display_name": "egetra",
131 | "language": "python",
132 | "name": "python3"
133 | },
134 | "language_info": {
135 | "codemirror_mode": {
136 | "name": "ipython",
137 | "version": 3
138 | },
139 | "file_extension": ".py",
140 | "mimetype": "text/x-python",
141 | "name": "python",
142 | "nbconvert_exporter": "python",
143 | "pygments_lexer": "ipython3",
144 | "version": "3.8.15"
145 | },
146 | "orig_nbformat": 4
147 | },
148 | "nbformat": 4,
149 | "nbformat_minor": 2
150 | }
151 |
--------------------------------------------------------------------------------