Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
484 changes: 242 additions & 242 deletions lab2.md

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions lab2/annotation_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Модуль для генерации CSV аннотаций изображений."""

import csv
import os
from typing import List
from pathlib import Path


class AnnotationGenerator:
"""Класс для создания CSV аннотаций изображений."""

@staticmethod
def create_annotation_csv(csv_file: str, folder_path: str) -> None:
"""
Создает CSV файл с аннотацией изображений.

Args:
csv_file: Путь к CSV файлу для сохранения аннотации
folder_path: Путь к папке с изображениями

Raises:
FileNotFoundError: Если папка с изображениями не найдена
PermissionError: Если нет прав для записи файла
UnicodeEncodeError: Если ошибка кодировки при записи
"""
try:
print(f"Создаю аннотацию {csv_file}...")

# Получаем список файлов изображений
image_files = AnnotationGenerator._get_image_files(folder_path)

# Создаем CSV файл
AnnotationGenerator._write_csv_file(csv_file, folder_path, image_files)

print(f"Создан CSV файл с {len(image_files)} записями")

except FileNotFoundError:
raise
except PermissionError:
raise
except UnicodeEncodeError:
raise
except Exception as e:
raise Exception(f"Ошибка при создании аннотации: {e}") from e

@staticmethod
def _get_image_files(folder_path: str) -> List[Path]:
"""
Получает список файлов изображений из указанной папки.

Args:
folder_path: Путь к папке с изображениями

Returns:
List[Path]: Список путей к файлам изображений
"""
image_files: List[Path] = []
for ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']:
image_files.extend(Path(folder_path).glob(f'*{ext}'))
image_files.extend(Path(folder_path).glob(f'*{ext.upper()}'))
return image_files

@staticmethod
def _write_csv_file(csv_file: str, folder_path: str, image_files: List[Path]) -> None:
"""
Записывает данные в CSV файл.

Args:
csv_file: Путь к CSV файлу
folder_path: Путь к папке с изображениями
image_files: Список путей к изображениям
"""
with open(csv_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Абсолютный путь', 'Относительный путь'])

for img_path in image_files:
abs_path = str(img_path.absolute())
rel_path = str(img_path.relative_to(folder_path))
writer.writerow([abs_path, rel_path])
121 changes: 121 additions & 0 deletions lab2/image_downloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Модуль для скачивания изображений с использованием различных краулеров."""

import os
from typing import List, Optional
from icrawler.builtin import GoogleImageCrawler, BingImageCrawler


class ImageDownloader:
"""Класс для скачивания изображений с использованием различных источников."""

def __init__(self, folder_path: str) -> None:
"""
Инициализирует загрузчик изображений.

Args:
folder_path: Путь к папке для сохранения изображений
"""
self.folder_path = folder_path
self._create_directory()

def _create_directory(self) -> None:
"""Создает директорию для сохранения изображений, если она не существует."""
try:
os.makedirs(self.folder_path, exist_ok=True)
except PermissionError as e:
raise PermissionError(f"Нет прав для создания директории {self.folder_path}") from e
except Exception as e:
raise Exception(f"Ошибка при создании директории {self.folder_path}: {e}") from e

def download_with_bing(self, keyword: str, count: int, min_size: tuple = (100, 100)) -> bool:
"""
Скачивает изображения с помощью Bing Image Crawler.

Args:
keyword: Ключевое слово для поиска
count: Количество изображений для скачивания
min_size: Минимальный размер изображений

Returns:
bool: True если скачивание прошло успешно, иначе False
"""
try:
print("Пробую скачать изображения с помощью Bing...")
bing_crawler = BingImageCrawler(storage={'root_dir': self.folder_path})
bing_crawler.crawl(
keyword=keyword,
max_num=count,
min_size=min_size
)
return True
except Exception as e:
print(f"Bing не сработал: {e}")
return False

def download_with_google(self, keyword: str, count: int, min_size: tuple = (100, 100)) -> bool:
"""
Скачивает изображения с помощью Google Image Crawler.

Args:
keyword: Ключевое слово для поиска
count: Количество изображений для скачивания
min_size: Минимальный размер изображений

Returns:
bool: True если скачивание прошло успешно, иначе False
"""
try:
print("Пробую скачать изображения с помощью Google...")
google_crawler = GoogleImageCrawler(
storage={'root_dir': self.folder_path},
feeder_threads=1,
parser_threads=1,
downloader_threads=2
)
google_crawler.crawl(
keyword=keyword,
max_num=count,
min_size=min_size
)
return True
except Exception as e:
print(f"Google не сработал: {e}")
return False

def create_test_files(self, count: int) -> None:
"""
Создает тестовые файлы, если не удалось скачать изображения.

Args:
count: Количество тестовых файлов для создания
"""
try:
print("Создаю тестовые файлы...")
for i in range(min(count, 10)):
test_file_path = os.path.join(self.folder_path, f"dog_{i + 1}.jpg")
with open(test_file_path, 'wb') as f:
f.write(b'test')
print(f"Создано {min(count, 10)} тестовых файлов")
except Exception as e:
raise Exception(f"Ошибка при создании тестовых файлов: {e}") from e

def download_images(self, keyword: str, count: int) -> None:
"""
Основной метод для скачивания изображений.

Args:
keyword: Ключевое слово для поиска
count: Количество изображений для скачивания
"""
print(f"Начинаю скачивание {count} фото...")

# Пробуем Bing - он обычно стабильнее
success = self.download_with_bing(keyword, count)

# Если папка пустая, пробуем Google
if not success or len(os.listdir(self.folder_path)) == 0:
success = self.download_with_google(keyword, count)

# Если все равно пусто, создаем тестовые файлы
if not success or len(os.listdir(self.folder_path)) == 0:
self.create_test_files(count)
78 changes: 78 additions & 0 deletions lab2/image_iterator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Модуль для работы с итератором изображений."""

import csv
from typing import List, Iterator
from pathlib import Path


class ImageIterator:
"""Итератор для перебора путей к изображениям из CSV файла."""

def __init__(self, csv_file: str) -> None:
"""
Инициализирует итератор изображений.

Args:
csv_file: Путь к CSV файлу с аннотацией изображений

Raises:
FileNotFoundError: Если файл не найден
PermissionError: Если нет прав для чтения файла
UnicodeDecodeError: Если ошибка кодировки файла
csv.Error: Если ошибка при чтении CSV файла
"""
self.csv_file = csv_file
self.index = 0
self.paths: List[str] = []

try:
self._load_paths_from_csv()
except FileNotFoundError:
raise
except PermissionError:
raise
except UnicodeDecodeError:
raise
except csv.Error as e:
raise csv.Error(f"Ошибка при чтении CSV файла {csv_file}: {e}") from e
except Exception as e:
raise Exception(f"Неожиданная ошибка при инициализации итератора: {e}") from e

def _load_paths_from_csv(self) -> None:
"""Загружает пути из CSV файла."""
try:
with open(self.csv_file, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader) # Пропускаем заголовок
self.paths = [row[0] for row in reader]
except FileNotFoundError as e:
raise FileNotFoundError(f"Файл {self.csv_file} не найден") from e
except PermissionError as e:
raise PermissionError(f"Нет прав для чтения файла {self.csv_file}") from e
except UnicodeDecodeError as e:
raise UnicodeDecodeError(f"Ошибка кодировки файла {self.csv_file}") from e

def __iter__(self) -> Iterator[str]:
"""
Возвращает итератор.

Returns:
Iterator[str]: Итератор по путям к изображениям
"""
return self

def __next__(self) -> str:
"""
Возвращает следующий путь к изображению.

Returns:
str: Путь к следующему изображению

Raises:
StopIteration: Когда пути закончились
"""
if self.index < len(self.paths):
path = self.paths[self.index]
self.index += 1
return path
raise StopIteration
87 changes: 87 additions & 0 deletions lab2/lab2_var4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Основной модуль программы для скачивания и обработки изображений собак."""

import argparse
import sys
from pathlib import Path

# Импорт собственных модулей
from image_downloader import ImageDownloader
from annotation_generator import AnnotationGenerator
from image_iterator import ImageIterator


def parse_arguments() -> argparse.Namespace:
"""
Парсит аргументы командной строки.

Returns:
argparse.Namespace: Объект с аргументами
"""
parser = argparse.ArgumentParser(description='Скачать ч/б фото собак')
parser.add_argument('--folder', required=True, help='Папка для сохранения')
parser.add_argument('--csv', required=True, help='CSV файл аннотации')
parser.add_argument('--count', type=int, default=50, help='Количество фото')
return parser.parse_args()


def test_iterator(csv_file: str, max_items: int = 5) -> None:
"""
Тестирует работу итератора изображений.

Args:
csv_file: Путь к CSV файлу с аннотацией
max_items: Максимальное количество элементов для вывода
"""
try:
print("\nТест итератора (первые несколько путей):")
iterator = ImageIterator(csv_file)

for i, path in enumerate(iterator):
if i >= max_items:
break
print(f" {path}")

except FileNotFoundError as e:
print(f"Ошибка: {e}")
except Exception as e:
print(f"Ошибка при тестировании итератора: {e}")


def main() -> None:
"""Основная функция программы."""
try:
# Парсинг аргументов
args = parse_arguments()

# Скачивание изображений
downloader = ImageDownloader(args.folder)
downloader.download_images('dogs on black and white background', args.count)

# Создание аннотации
AnnotationGenerator.create_annotation_csv(args.csv, args.folder)

# Проверка количества скачанных файлов
image_files = list(Path(args.folder).glob('*.*'))
print(f"Скачано файлов: {len(image_files)}")

# Тест итератора
test_iterator(args.csv)

print("\nПрограмма успешно завершена!")

except KeyboardInterrupt:
print("\nПрограмма прервана пользователем")
sys.exit(1)
except FileNotFoundError as e:
print(f"Ошибка: Файл не найден - {e}")
sys.exit(1)
except PermissionError as e:
print(f"Ошибка доступа: {e}")
sys.exit(1)
except Exception as e:
print(f"Неожиданная ошибка: {e}")
sys.exit(1)


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