Spaces:
Sleeping
Sleeping
ivanovot
commited on
Commit
·
ba31077
1
Parent(s):
eea5fab
init
Browse files- CoinCounter/__pycache__/main.cpython-312.pyc +0 -0
- CoinCounter/__pycache__/model.cpython-312.pyc +0 -0
- CoinCounter/__pycache__/utils.cpython-312.pyc +0 -0
- CoinCounter/model.py +24 -0
- CoinCounter/models/model.onnx +3 -0
- CoinCounter/utils.py +132 -0
- app.py +64 -0
- data/20220529_210820_jpg.rf.81aa1bddbb0de0380547fce646a35a61.jpg +0 -0
- data/IMG_2094_jpg.rf.3e70f255e403911a57c01204cdce4967.jpg +0 -0
- data/IMG_2157_mp4-3_jpg.rf.0ca96998face2ad86f6f36e95ed27adb.jpg +0 -0
- data/IMG_2162_jpeg_jpg.rf.5290164c3c5aaa3c2d342e4d691ef8b4.jpg +0 -0
- data/S3_jpg.rf.8ae0df6b09f82d964bf4ec026d8abd74.jpg +0 -0
- data/conveyerBoxes-AdobeStock_193267768_mov-10_jpg.rf.f65b8f806082924cbacd0fa8d78c888c.jpg +0 -0
- data/test1.jpg +0 -0
- data/test2.jpg +0 -0
- data/test3.jpg +0 -0
- main.py +71 -0
- requirements.txt +0 -0
- temp_image.jpg +0 -0
- tutorial.ipynb +0 -0
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
|
|