Skip to content

Commit 7add996

Browse files
authored
v2.1.0 release
# Release Notes ## New Features - **Add `frames_extractor`**: - Added `all_frames` flag to `best_frames_extractor`. - Implemented `all_frames` logic in `best_frames_extractor` and updated tests accordingly. - Added `all_frames` flag to `service_initializer` and updated tests. - **New Sections and Enhancements in Documentation**: - Added section Architecture and update README - Created and updated `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, and `pull_request_template.md`. - Added visualizations and demo presentation image to the README. - Compressed static files and images for better performance. - Updated issue templates for bug reports and feature requests. ## Bug Fixes - Fixed logo text color. - Fixed href links in various sections. - Fixed typos and improved text in both English and Polish READMEs. - Fixed code_of_conduct href in `CONTRIBUTING.md`. - Removed non-functional README.md badge. - Fixed typo in extractors. ## Refactoring and Improvements - Replaced `|` with `typing.Union`. - Renamed methods for consistency: - `get_extractor()` -> `create_extractor()` in `ExtractorFactory`. - Changed method name in `OpenCVVideo` from `get_next_video_frames` to `get_next_frames`. - Moved normalizing images from evaluator to extractors. - Added `normalize_images` method as an abstract method in `image_processors.py`. - Moved `ServiceShutdownSignal` inside `DockerManager`. - Made `ServiceInitializer` attributes protected. - Added license to docstrings. - Added badges and fixed language links. ## Other Changes - Created and updated various markdown files in the `.github` directory. - Improved docker configurations by adding `.dockerignore`.
2 parents 9ade05e + c4119c4 commit 7add996

26 files changed

+487
-117
lines changed

.github/README.pl.md

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,25 @@
7979
<p>Output: Obrazy zapisane jako <code>.jpg</code>.</p>
8080
</ol>
8181
</details>
82+
<br>
83+
<details>
84+
<summary>
85+
<strong>🆕 Frames Extraction 🖼️🖼️🖼️</strong>
86+
<blockquote>Zamienia pliki video na klatki.</blockquote>
87+
</summary>
88+
<p>Modyfikuje <code>best_frames_extractor</code> poprzez pominięcie części z AI/ocenianiem klatek.</p>
89+
<code>python start.py best_frames_extractor --all_frames</code>
90+
<ol>
91+
<p>Input: Folder z plikami video <code>.mp4</code>.</p>
92+
<li>Bierze pierwsze video ze wskazanej lokalizacji.</li>
93+
<li>
94+
Dzieli wideo na klatki. Klatki są brane co 1 sekundę wideo.
95+
Klatki są przetwarzane w batchach(seriach).
96+
</li>
97+
<li>Zapisuje wszystkie klatki w wybranej lokalizacji.</li>
98+
<p>Output: Klatki zapisane jako <code>.jpg</code>.</p>
99+
</ol>
100+
</details>
82101
</div>
83102
<div id="installation">
84103
<h2>💿 Instalacja</h2>
@@ -199,23 +218,32 @@
199218
<h2>💡O projekcie:</h2>
200219
<div id="contents">
201220
<h3>Spis treści:</h3>
202-
<a href="#how-it-works">Jak to działa</a><br>
203-
&nbsp&nbsp&nbsp&nbsp<a href="#input">Input modelu</a><br>
204-
&nbsp&nbsp&nbsp&nbsp<a href="#output">Wyniki oceniania obrazów</a><br>
205-
&nbsp&nbsp&nbsp&nbsp<a href="#classes">Klasy estetyczne</a><br>
206-
&nbsp&nbsp&nbsp&nbsp<a href="#calculating-mean">Obliczanie ostatecznej oceny obrazu</a><br>
207-
<a href="#implementation">Jak to jest zaimplementowane w skrócie</a><br>
208-
&nbsp&nbsp&nbsp&nbsp<a href="#model-architecture">Architektura modelu</a><br>
209-
&nbsp&nbsp&nbsp&nbsp<a href="#weights">Wagi modelu</a><br>
210-
&nbsp&nbsp&nbsp&nbsp<a href="#normalization">Normalizacja obrazów</a><br>
211-
&nbsp&nbsp&nbsp&nbsp<a href="#predictions">Przewidywanie przynależności do klas</a><br>
212-
&nbsp&nbsp&nbsp&nbsp<a href="#mean-calculation">Obliczanie średniej ważonej</a><br>
213-
<a href="#1vs2">v1.0 vs v2.0</a><br>
214-
<a href="#build-with">Użyte technologie</a><br>
215-
<a href="#tests">Testy</a><br>
216-
&nbsp&nbsp&nbsp&nbsp<a href="#unit">jednostkowe</a><br>
217-
&nbsp&nbsp&nbsp&nbsp<a href="#integration">integracyjne</a><br>
218-
&nbsp&nbsp&nbsp&nbsp<a href="#e2e">e2e</a><br>
221+
<ul>
222+
<li><a href="#how-it-works">Jak to działa</a></li>
223+
<ul>
224+
<li><a href="#input">Input modelu</a></li>
225+
<li><a href="#output">Wyniki oceniania obrazów</a></li>
226+
<li><a href="#classes">Klasy estetyczne</a></li>
227+
<li><a href="#calculating-mean">Obliczanie ostatecznej oceny obrazu</a></li>
228+
</ul>
229+
<li><a href="#implementation">Implementacja w skrócie</a></li>
230+
<ul>
231+
<li><a href="#model-architecture">Architektura modelu</a></li>
232+
<li><a href="#weights">Wagi modelu</a></li>
233+
<li><a href="#normalization">Normalizacja obrazów</a></li>
234+
<li><a href="#predictions">Przewidywanie przynależności do klas</a></li>
235+
<li><a href="#mean-calculation">Obliczanie średniej ważonej</a></li>
236+
</ul>
237+
<li><a href="#1vs2">v1.0 vs v2.0</a></li>
238+
<li><a href="#architecture">Architektura</a></li>
239+
<li><a href="#build-with">Użyte technologie</a></li>
240+
<li><a href="#tests">Testy</a></li>
241+
<ul>
242+
<li><a href="#unit">Jednostkowe</a></li>
243+
<li><a href="#integration">Integracyjne</a></li>
244+
<li><a href="#e2e">E2E</a></li>
245+
</ul>
246+
</ul>
219247
</div>
220248
<div id="how-it-works">
221249
<h2>📐 Jak to działa</h2>
@@ -372,6 +400,10 @@
372400
</ul>
373401
<img src="../static/performance.png" height="200">
374402
</div>
403+
<div id="architecture">
404+
<h2>Architektura</h2>
405+
<img src="../static/architecture.jpg" width="1000" style="border-radius: 10px;">
406+
</div>
375407
<div id="build-with">
376408
<h2>🛠️ Użyte technologie</h2>
377409
<ul>

README.md

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@
7979
<p>Output: Images saved as <code>.jpg</code>.</p>
8080
</ol>
8181
</details>
82+
<br>
83+
<details>
84+
<summary>
85+
<strong>🆕 Frames Extraction 🖼️🖼️🖼️</strong>
86+
<blockquote>Extract and return frames from a video.</blockquote>
87+
</summary>
88+
<p>Modifying <code>best_frames_extractor</code> by skipping AI evaluation part.</p>
89+
<code>python start.py best_frames_extractor --all_frames</code>
90+
<ol>
91+
<p>Input: Folder with <code>.mp4</code> video files.</p>
92+
<li>Takes the first video from the specified location.</li>
93+
<li>
94+
Splits the video into frames.
95+
Frames are taken at 1-second intervals.
96+
Frames are processed in batches.
97+
</li>
98+
<li>Saves all frames in the chosen location.</li>
99+
<p>Output: Frames saved as <code>.jpg</code>.</p>
100+
</ol>
101+
</details>
82102
</div>
83103
<div id="installation">
84104
<h2>💿 Installation</h2>
@@ -201,23 +221,32 @@
201221
<h2>💡 About:</h2>
202222
<div id="contents">
203223
<h3>Table of Contents:</h3>
204-
<a href="#how-it-works">How it works</a><br>
205-
&nbsp&nbsp&nbsp&nbsp<a href="#input">Model Input</a><br>
206-
&nbsp&nbsp&nbsp&nbsp<a href="#output">Image Rating Results</a><br>
207-
&nbsp&nbsp&nbsp&nbsp<a href="#classes">Aesthetic Classes</a><br>
208-
&nbsp&nbsp&nbsp&nbsp<a href="#calculating-mean">Calculating the Final Image Score</a><br>
209-
<a href="#implementation">Implementation in Brief</a><br>
210-
&nbsp&nbsp&nbsp&nbsp<a href="#model-architecture">Model Architecture</a><br>
211-
&nbsp&nbsp&nbsp&nbsp<a href="#weights">Pre-trained Weights</a><br>
212-
&nbsp&nbsp&nbsp&nbsp<a href="#normalization">Image Normalization</a><br>
213-
&nbsp&nbsp&nbsp&nbsp<a href="#predictions">Class Predictions</a><br>
214-
&nbsp&nbsp&nbsp&nbsp<a href="#mean-calculation">Weighted Mean Calculation</a><br>
215-
<a href="#1vs2">v1.0 vs v2.0</a><br>
216-
<a href="#build-with">Build with</a><br>
217-
<a href="#tests">Tests</a><br>
218-
&nbsp&nbsp&nbsp&nbsp<a href="#unit">unit</a><br>
219-
&nbsp&nbsp&nbsp&nbsp<a href="#integration">integration</a><br>
220-
&nbsp&nbsp&nbsp&nbsp<a href="#e2e">e2e</a><br>
224+
<ul>
225+
<li><a href="#how-it-works">How it works</a></li>
226+
<ul>
227+
<li><a href="#input">Model Input</a></li>
228+
<li><a href="#output">Image Rating Results</a></li>
229+
<li><a href="#classes">Aesthetic Classes</a></li>
230+
<li><a href="#calculating-mean">Calculating the Final Image Score</a></li>
231+
</ul>
232+
<li><a href="#implementation">Implementation in Brief</a></li>
233+
<ul>
234+
<li><a href="#model-architecture">Model Architecture</a></li>
235+
<li><a href="#weights">Pre-trained Weights</a></li>
236+
<li><a href="#normalization">Image Normalization</a></li>
237+
<li><a href="#predictions">Class Predictions</a></li>
238+
<li><a href="#mean-calculation">Weighted Mean Calculation</a></li>
239+
</ul>
240+
<li><a href="#1vs2">v1.0 vs v2.0</a></li>
241+
<li><a href="#architecture">Architecture</a></li>
242+
<li><a href="#build-with">Build with</a></li>
243+
<li><a href="#tests">Tests</a></li>
244+
<ul>
245+
<li><a href="#unit">unit</a></li>
246+
<li><a href="#integration">integration</a></li>
247+
<li><a href="#e2e">e2e</a></li>
248+
</ul>
249+
</ul>
221250
</div>
222251
<div id="how-it-works">
223252
<h2>📐 How it Works</h2>
@@ -352,6 +381,10 @@
352381
</ul>
353382
<img src="static/performance.png" height="200">
354383
</div>
384+
<div id="architecture">
385+
<h2>Architecture</h2>
386+
<img src="static/architecture.jpg" width="1000" style="border-radius: 10px;">
387+
</div>
355388
<div id="build-with">
356389
<h2>🛠️ Built with</h2>
357390
<ul>

config.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
1-
"""Main configuration dataclass for extractor service manager tool."""
1+
"""
2+
Main configuration dataclass for extractor service manager tool.
3+
LICENSE
4+
=======
5+
Copyright (C) 2024 Bartłomiej Flis
6+
7+
This program is free software: you can redistribute it and/or modify
8+
it under the terms of the GNU General Public License as published by
9+
the Free Software Foundation, either version 3 of the License, or
10+
(at your option) any later version.
11+
12+
This program is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU General Public License for more details.
16+
17+
You should have received a copy of the GNU General Public License
18+
along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
"""
220
from dataclasses import dataclass
321
from pathlib import Path
422

extractor_service/app/extractor_manager.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
"""
22
This module provides manager class for running extractors and
33
managing extraction process lifecycle.
4+
LICENSE
5+
=======
6+
Copyright (C) 2024 Bartłomiej Flis
7+
8+
This program is free software: you can redistribute it and/or modify
9+
it under the terms of the GNU General Public License as published by
10+
the Free Software Foundation, either version 3 of the License, or
11+
(at your option) any later version.
12+
13+
This program is distributed in the hope that it will be useful,
14+
but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
GNU General Public License for more details.
17+
18+
You should have received a copy of the GNU General Public License
19+
along with this program. If not, see <https://www.gnu.org/licenses/>.
420
"""
521
import logging
622
from typing import Type
@@ -49,7 +65,7 @@ def start_extractor(cls, background_tasks: BackgroundTasks, config: ExtractorCon
4965
"""
5066
cls._config = config
5167
cls._check_is_already_extracting()
52-
extractor_class = ExtractorFactory.get_extractor(extractor_name)
68+
extractor_class = ExtractorFactory.create_extractor(extractor_name)
5369
background_tasks.add_task(cls.__run_extractor, extractor_class, extractor_name)
5470
message = f"'{extractor_name}' started."
5571
return message

extractor_service/app/extractors.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@
55
- Extractors:
66
- BestFramesExtractor: For extracting best frames from all videos from any directory.
77
- TopImagesExtractor: For extracting images with top percent evaluating from any directory.
8+
LICENSE
9+
=======
10+
Copyright (C) 2024 Bartłomiej Flis
11+
12+
This program is free software: you can redistribute it and/or modify
13+
it under the terms of the GNU General Public License as published by
14+
the Free Software Foundation, either version 3 of the License, or
15+
(at your option) any later version.
16+
17+
This program is distributed in the hope that it will be useful,
18+
but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
GNU General Public License for more details.
21+
22+
You should have received a copy of the GNU General Public License
23+
along with this program. If not, see <https://www.gnu.org/licenses/>.
824
"""
925
from concurrent.futures import ThreadPoolExecutor
1026
from pathlib import Path
@@ -88,17 +104,17 @@ def _list_input_directory_files(self, extensions: tuple[str],
88104
logger.debug("Listed file paths: %s", files)
89105
return files
90106

91-
def _evaluate_images(self, images: list[np.ndarray]) -> np.array:
107+
def _evaluate_images(self, normalized_images: np.ndarray) -> np.array:
92108
"""
93109
Rating all images in provided images batch using already initialized image evaluator.
94110
95111
Args:
96-
images (list[np.ndarray]): List of images in numpy ndarrays.
112+
normalized_images (list[np.ndarray]): Already normalized images np.ndarray for evaluating.
97113
98114
Returns:
99115
np.array: Array with images scores in given images order.
100116
"""
101-
scores = np.array(self._image_evaluator.evaluate_images(images))
117+
scores = np.array(self._image_evaluator.evaluate_images(normalized_images))
102118
return scores
103119

104120
@staticmethod
@@ -139,6 +155,21 @@ def _save_images(self, images: list[np.ndarray]) -> None:
139155
for future in futures:
140156
future.result()
141157

158+
@staticmethod
159+
def _normalize_images(images: list[np.ndarray], target_size: tuple[int, int]) -> np.ndarray:
160+
"""
161+
Normalize all images in given list to target size for further operations.
162+
163+
Args:
164+
images (list[np.ndarray]): List of np.ndarray images to normalize.
165+
target_size (tuple[int, int]): Images will be normalized to this size.
166+
167+
Returns:
168+
np.ndarray: All images as a one numpy array.
169+
"""
170+
normalized_images = OpenCVImage.normalize_images(images, target_size)
171+
return normalized_images
172+
142173
@staticmethod
143174
def _add_prefix(prefix: str, path: Path) -> Path:
144175
"""
@@ -169,7 +200,7 @@ def _signal_readiness_for_shutdown() -> None:
169200
class ExtractorFactory:
170201
"""Extractor factory for getting extractors class by their names."""
171202
@staticmethod
172-
def get_extractor(extractor_name: str) -> Type[Extractor]:
203+
def create_extractor(extractor_name: str) -> Type[Extractor]:
173204
"""
174205
Match extractor class by its name and return its class.
175206
@@ -221,12 +252,16 @@ def _extract_best_frames(self, video_path: Path) -> list[np.ndarray]:
221252
list[np.ndarray]: List of best images(frames) from the given video.
222253
"""
223254
best_frames = []
224-
frames_batch_generator = OpenCVVideo.get_next_video_frames(video_path, self._config.batch_size)
255+
frames_batch_generator = OpenCVVideo.get_next_frames(video_path, self._config.batch_size)
225256
for frames in frames_batch_generator:
226257
if not frames:
227258
continue
228-
logger.debug("Frames pack generated.")
229-
scores = self._evaluate_images(frames)
259+
logger.debug("Frames batch generated.")
260+
if self._config.all_frames:
261+
best_frames.extend(frames)
262+
continue
263+
normalized_images = self._normalize_images(frames, self._config.target_image_size)
264+
scores = self._evaluate_images(normalized_images)
230265
selected_frames = self._get_best_frames(frames, scores,
231266
self._config.compering_group_size)
232267
best_frames.extend(selected_frames)
@@ -268,7 +303,8 @@ def process(self) -> None:
268303
for batch_index in range(0, len(images_paths), self._config.batch_size):
269304
batch = images_paths[batch_index:batch_index + self._config.batch_size]
270305
images = self._read_images(batch)
271-
scores = self._evaluate_images(images)
306+
normalized_images = self._normalize_images(images, self._config.target_image_size)
307+
scores = self._evaluate_images(normalized_images)
272308
top_images = self._get_top_percent_images(images, scores,
273309
self._config.top_images_percent)
274310
self._save_images(top_images)

0 commit comments

Comments
 (0)