ivanovot commited on
Commit
ba31077
·
1 Parent(s): eea5fab
CoinCounter/__pycache__/main.cpython-312.pyc ADDED
Binary file (395 Bytes). View file
 
CoinCounter/__pycache__/model.cpython-312.pyc ADDED
Binary file (1.39 kB). View file
 
CoinCounter/__pycache__/utils.cpython-312.pyc ADDED
Binary file (6.72 kB). View file
 
CoinCounter/model.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from ultralytics import YOLO
3
+ from .utils import Result
4
+
5
+ # Загрузка модели YOLO
6
+ model = YOLO('CoinCounter/models/model.onnx', task='detect',verbose=False)
7
+
8
+ def predict(path='data', conf=0.5, iou=0.5):
9
+ """
10
+ Выполняет предсказание с помощью модели YOLO.
11
+
12
+ :param path: Путь к изображению или директории с изображениями для предсказания.
13
+ :param conf: Порог уверенности для предсказаний (0.0 - 1.0). Чем выше значение, тем меньше ложных срабатываний.
14
+ :param iou: Порог перекрытия для фильтрации предсказанных рамок (0.0 - 1.0). Чем выше значение, тем более строгий отбор.
15
+ :return: Результаты предсказания в формате объекта Result.
16
+ """
17
+ # Выполнение предсказания с заданными параметрами
18
+ results = model.predict(
19
+ path,
20
+ conf=conf, # Параметр уверенности
21
+ iou=iou, # Параметр IoU
22
+ verbose=False
23
+ )
24
+ return Result(results)
CoinCounter/models/model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b2438c89dd271316675dbd289595bc85d93eb251c107db39fbef86a8fcc8d35e
3
+ size 44732869
CoinCounter/utils.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import matplotlib.pyplot as plt
3
+ from PIL import Image, ImageDraw
4
+ import numpy as np
5
+ import matplotlib.colors as mcolors
6
+
7
+ class_map = {
8
+ 0: 'Dime',
9
+ 1: 'Nickel',
10
+ 2: 'Penny',
11
+ 3: 'Quarter',
12
+ 4: 'fifty',
13
+ 5: 'five',
14
+ 6: 'hundred',
15
+ 7: 'one',
16
+ 8: 'ten',
17
+ 9: 'twenty'
18
+ }
19
+
20
+ value_map = {
21
+ 'Penny': 0.01,
22
+ 'Nickel': 0.05,
23
+ 'Dime': 0.10,
24
+ 'Quarter': 0.25,
25
+ 'one': 1.00,
26
+ 'five': 5.00,
27
+ 'ten': 10.00,
28
+ 'twenty': 20.00,
29
+ 'fifty': 50.00,
30
+ 'hundred': 100.00
31
+ }
32
+
33
+ nomimals = ['Dime', 'Nickel', 'Penny', 'Quarter', 'fifty', 'five', 'hundred', 'one', 'ten', 'twenty']
34
+
35
+ class Result:
36
+ def __init__(self, results, nomimals = nomimals, class_map = class_map, value_map = value_map):
37
+ self.results = results
38
+ self.nomimals = nomimals
39
+ self.class_map = class_map
40
+ self.value_map = value_map
41
+
42
+ # Список объектов по классам
43
+ self.objects = [[int(box.cls) for box in result.boxes] for result in self.results]
44
+
45
+ # Создаем DataFrame с подсчетом объектов и их общей "стоимостью"
46
+ self.df = pd.DataFrame([
47
+ {class_map[obj]: lst.count(obj) for obj in class_map} for lst in self.objects
48
+ ])
49
+ self.df['total'] = self.df.apply(lambda row: sum(row[obj] * value_map[obj] for obj in value_map), axis=1)
50
+
51
+ def __len__(self):
52
+ return len(self.objects)
53
+
54
+ def total(self, id=None):
55
+ if id is None:
56
+ return round(self.df.total.sum(), 2)
57
+ elif isinstance(id, int):
58
+ return round(self.df.iloc[id].total, 2)
59
+ else:
60
+ print('Please use an integer for the ID.')
61
+
62
+ def image(self, index=0):
63
+ """
64
+ Возвращает изображение с разметкой объектов.
65
+ :param index: Индекс изображения в results.
66
+ :return: PIL.Image с размеченными объектами.
67
+ """
68
+ if index >= len(self.results):
69
+ raise IndexError("Индекс выходит за пределы диапазона.")
70
+
71
+ # Получаем изображение и разметку
72
+ result = self.results[index]
73
+ img = result.orig_img
74
+ boxes = result.boxes
75
+
76
+ # Конвертируем изображение из BGR в RGB
77
+ img_rgb = img[..., ::-1] # Меняем порядок каналов с BGR на RGB
78
+
79
+ # Конвертируем изображение в PIL.Image
80
+ img_pil = Image.fromarray(img_rgb)
81
+ draw = ImageDraw.Draw(img_pil)
82
+
83
+ # Определяем цвета для объектов
84
+ object_colors = list(mcolors.TABLEAU_COLORS.values())
85
+
86
+ # Если есть разметка, добавляем её
87
+ if boxes is not None:
88
+ xyxy = boxes.xyxy.cpu().numpy() # Координаты [x1, y1, x2, y2]
89
+ conf = boxes.conf.cpu().numpy() # Уверенность
90
+ cls = boxes.cls.cpu().numpy() # Классы
91
+
92
+ for j in range(len(xyxy)):
93
+ x1, y1, x2, y2 = xyxy[j]
94
+ class_id = int(cls[j])
95
+ class_name = result.names[class_id]
96
+ confidence = conf[j]
97
+
98
+ # Присваиваем цвет для класса
99
+ color = object_colors[class_id % len(object_colors)]
100
+
101
+ # Рисуем прямоугольник
102
+ draw.rectangle([(x1, y1), (x2, y2)], outline=color, width=3)
103
+
104
+ # Добавляем подпись (класс и уверенность)
105
+ text = f'{class_name} {confidence:.2f}'
106
+ draw.text((x1, y1 - 10), text, fill=color)
107
+
108
+ return img_pil
109
+ def show(self, num_cols=1, scale=1):
110
+ """
111
+ Функция для создания коллажа с размеченными объектами на фотографиях.
112
+ :param num_cols: Количество столбцов в коллаже
113
+ :param scale: Масштаб для отображения изображений
114
+ """
115
+ num_images = len(self.results)
116
+ num_rows = (num_images + num_cols - 1) // num_cols # Округление вверх
117
+
118
+ fig, axes = plt.subplots(num_rows, num_cols, figsize=(num_cols * 5 * scale, num_rows * 5 * scale))
119
+ axes = axes.flatten() if isinstance(axes, (list, np.ndarray)) else [axes]
120
+
121
+ for i, ax in enumerate(axes[:num_images]):
122
+ img_pil = self.image(i) # Получаем изображение с разметкой
123
+ ax.imshow(img_pil)
124
+ ax.axis('off')
125
+
126
+ # Убираем лишние оси
127
+ for ax in axes[num_images:]:
128
+ ax.axis('off')
129
+
130
+ plt.tight_layout()
131
+ plt.show()
132
+
app.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from CoinCounter.model import predict
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+
7
+ # Получаем все пути к изображениям в папке 'data'
8
+ image_paths = [os.path.join('data', filename) for filename in os.listdir('data') if filename.endswith(('.png', '.jpg', '.jpeg'))]
9
+
10
+ def process_image(image, conf, iou):
11
+ # Сохраняем изображение во временный буфер для обработки
12
+ image_path = "temp_image.jpg"
13
+ image.save(image_path)
14
+
15
+ # Выполняем предсказание с моделью с заданными параметрами
16
+ results = predict(path=image_path, conf=conf, iou=iou)
17
+
18
+ # Получаем изображение с размеченными объектами
19
+ annotated_image = results.image(0)
20
+
21
+ # Конвертируем результат в формат, подходящий для вывода
22
+ annotated_image = np.array(annotated_image)
23
+
24
+ # Получаем сумму денег
25
+ total_sum = results.total(0)
26
+
27
+ # Извлекаем количество монет из DataFrame
28
+ df = results.df
29
+ coin_counts = df.iloc[0, :-1] # исключаем последнюю колонку 'total'
30
+
31
+ # Формируем строку с количеством монет
32
+ coin_info = "\n".join([f"{coin}: {int(count)}" for coin, count in zip(coin_counts.index, coin_counts) if count > 0])
33
+
34
+ # Формируем итоговый текст
35
+ result_text = f"Общая сумма на изображении: {total_sum:.2f} $\n\nКоличество объектов:\n{coin_info}"
36
+
37
+ return annotated_image, result_text
38
+
39
+ # Определяем интерфейс Gradio
40
+ with gr.Blocks() as demo:
41
+ gr.Markdown("## Coin Counter")
42
+
43
+ with gr.Row():
44
+ # Левый блок (ввод)
45
+ with gr.Column():
46
+ input_image = gr.Image(label="Загрузите изображение", type="pil")
47
+ conf_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, value=0.3, label="Порог уверенности (conf)")
48
+ iou_slider = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, value=0.45, label="Порог перекрытия (IoU)")
49
+
50
+ # Правый блок (вывод)
51
+ with gr.Column():
52
+ output_image = gr.Image(label="Размеченное изображение")
53
+ output_text = gr.Textbox(label="Результат", lines=5)
54
+
55
+ # Обработка изображения автоматически при изменении изображения или тумблеров
56
+ input_image.change(process_image, inputs=[input_image, conf_slider, iou_slider], outputs=[output_image, output_text])
57
+ conf_slider.change(process_image, inputs=[input_image, conf_slider, iou_slider], outputs=[output_image, output_text])
58
+ iou_slider.change(process_image, inputs=[input_image, conf_slider, iou_slider], outputs=[output_image, output_text])
59
+
60
+ # Добавление примеров из папки 'data'
61
+ gr.Examples(examples=image_paths, inputs=input_image)
62
+
63
+ # Запускаем интерфейс
64
+ demo.launch()
data/20220529_210820_jpg.rf.81aa1bddbb0de0380547fce646a35a61.jpg ADDED
data/IMG_2094_jpg.rf.3e70f255e403911a57c01204cdce4967.jpg ADDED
data/IMG_2157_mp4-3_jpg.rf.0ca96998face2ad86f6f36e95ed27adb.jpg ADDED
data/IMG_2162_jpeg_jpg.rf.5290164c3c5aaa3c2d342e4d691ef8b4.jpg ADDED
data/S3_jpg.rf.8ae0df6b09f82d964bf4ec026d8abd74.jpg ADDED
data/conveyerBoxes-AdobeStock_193267768_mov-10_jpg.rf.f65b8f806082924cbacd0fa8d78c888c.jpg ADDED
data/test1.jpg ADDED
data/test2.jpg ADDED
data/test3.jpg ADDED
main.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tkinter as tk
2
+ from tkinter import filedialog, messagebox
3
+ from CoinCounter.model import predict
4
+ import os
5
+ import glob
6
+
7
+ class CoinCounterApp:
8
+ def __init__(self, root):
9
+ self.root = root
10
+ self.root.title("CoinCounter")
11
+ self.root.geometry("300x150")
12
+
13
+ # Кнопка для выбора файла
14
+ self.file_button = tk.Button(root, text="Выбрать файл", command=self.select_file)
15
+ self.file_button.pack(pady=10)
16
+
17
+ # Кнопка для выбора папки
18
+ self.folder_button = tk.Button(root, text="Выбрать папку", command=self.select_folder)
19
+ self.folder_button.pack(pady=10)
20
+
21
+ def select_file(self):
22
+ file_path = filedialog.askopenfilename(
23
+ title="Выберите файл",
24
+ filetypes=[("Image files", "*.jpg *.jpeg *.png")]
25
+ )
26
+ if file_path:
27
+ self.process_image(file_path)
28
+
29
+ def select_folder(self):
30
+ folder_path = filedialog.askdirectory(title="Выберите папку")
31
+ if folder_path:
32
+ if self.check_images_in_folder(folder_path):
33
+ self.process_image(folder_path)
34
+ else:
35
+ messagebox.showinfo("Информация", "В папке нет изображений")
36
+
37
+ def check_images_in_folder(self, folder_path):
38
+ image_files = glob.glob(os.path.join(folder_path, "*.jpg")) + \
39
+ glob.glob(os.path.join(folder_path, "*.jpeg")) + \
40
+ glob.glob(os.path.join(folder_path, "*.png"))
41
+ return len(image_files) > 0
42
+
43
+ def process_image(self, path):
44
+ # Показать окно с сообщением о процессе прогноза
45
+ self.processing_window = tk.Toplevel(self.root)
46
+ self.processing_window.title("Процесс")
47
+ self.processing_window.geometry("350x100") # Увеличиваем размер окна
48
+ tk.Label(self.processing_window, text="Идет процесс прогноза... пожалуйста, подождите", wraplength=300).pack(pady=30)
49
+ self.root.update_idletasks() # Обновить интерфейс перед выполнением длительной задачи
50
+
51
+ self.root.after(100, self.run_prediction, path) # Запуск функции предсказания через 100 мс
52
+
53
+ def run_prediction(self, path):
54
+ try:
55
+ # Запуск функции predict
56
+ results = predict(
57
+ path=path,
58
+ conf=0.3,
59
+ iou=0.3
60
+ )
61
+ total_amount = results.total()
62
+ self.processing_window.destroy() # Закрыть окно с процессом
63
+ messagebox.showinfo("Результат", f"Общая сумма: {total_amount:.2f}$")
64
+ except Exception as e:
65
+ self.processing_window.destroy() # Закрыть окно с процессом
66
+ messagebox.showerror("Ошибка", str(e))
67
+
68
+ if __name__ == "__main__":
69
+ root = tk.Tk()
70
+ app = CoinCounterApp(root)
71
+ root.mainloop()
requirements.txt ADDED
Binary file (2.57 kB). View file
 
temp_image.jpg ADDED
tutorial.ipynb ADDED
The diff for this file is too large to render. See raw diff