добил до конца получения результата по ссылке
Gitea Actions Demo / build_and_push (push) Has been cancelled Details

This commit is contained in:
artem 2026-02-07 21:14:32 +03:00
parent 3baab00f80
commit 90331a7036
8 changed files with 96 additions and 32 deletions

View File

@ -35,4 +35,5 @@ class ResultsView(ModelView):
"result", "result",
"path", "path",
"beerd", "beerd",
"probability"
] ]

View File

@ -57,12 +57,11 @@ class BreedsController(Controller):
recognizer_service: RecognizerService = inject.instance(RecognizerService) recognizer_service: RecognizerService = inject.instance(RecognizerService)
result = await recognizer_service.get_results(result_id) result = await recognizer_service.get_results(result_id)
attach_service: AtachmentService = inject.instance(AtachmentService) attach_service: AtachmentService = inject.instance(AtachmentService)
attachments = await attach_service.get_info_byid(session=None, attach_id= [result.attachment_id]) attachments = await attach_service.get_info_byid(session=None, attach_id=[result.attachment_id])
return Template( return Template(
template_name="share.html", template_name="share.html",
context={ context={
"result": result, "result": result,
"attachments": attachments, "attachment": attachments[0],
}, },
) )

View File

@ -0,0 +1,29 @@
"""3baab00
Revision ID: 081eb0827a55
Revises: bebaddef3e8d
Create Date: 2026-02-07 20:46:53.971562
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "081eb0827a55"
down_revision: str | None = "bebaddef3e8d"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("recognizer_results_beerds", sa.Column("probability", sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("recognizer_results_beerds", "probability")
# ### end Alembic commands ###

View File

@ -1,6 +1,3 @@
from server.modules.recognizer.repository.repository import ( from server.modules.recognizer.repository.repository import ARecognizerRepository, RecognizerRepository, ResultBeerds
ARecognizerRepository,
RecognizerRepository,
)
__all__ = ("RecognizerRepository", "ARecognizerRepository") __all__ = ("RecognizerRepository", "ARecognizerRepository", "ResultBeerds")

View File

@ -6,6 +6,7 @@ from sqlalchemy import (
Column, Column,
DateTime, DateTime,
ForeignKeyConstraint, ForeignKeyConstraint,
Integer,
String, String,
) )
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -62,3 +63,4 @@ class ResultBeerds(UJsonMixin):
id: str = field(metadata={"sa": Column(String(), primary_key=True, nullable=False)}) id: str = field(metadata={"sa": Column(String(), primary_key=True, nullable=False)})
recognizer_results_id: str = field(metadata={"sa": Column(String(), nullable=False)}) recognizer_results_id: str = field(metadata={"sa": Column(String(), nullable=False)})
beerd_id: str = field(metadata={"sa": Column(String(), nullable=False)}) beerd_id: str = field(metadata={"sa": Column(String(), nullable=False)})
probability: int = field(metadata={"sa": Column(Integer(), nullable=True)})

View File

@ -17,6 +17,12 @@ class ResultWithBeerds:
beerds: list[rm.ResultBeerds] beerds: list[rm.ResultBeerds]
@dataclass
class ResultBeerds:
beerd_id: str
probability: float
class ARecognizerRepository(metaclass=ABCMeta): class ARecognizerRepository(metaclass=ABCMeta):
@abstractmethod @abstractmethod
async def images_dogs(self) -> dict: async def images_dogs(self) -> dict:
@ -39,7 +45,7 @@ class ARecognizerRepository(metaclass=ABCMeta):
"""Получить **все** результаты (кэшируется).""" """Получить **все** результаты (кэшируется)."""
@abstractmethod @abstractmethod
async def create_result_with_beerds(self, result: rm.Results, beerd_ids: list[str]) -> None: async def create_result_with_beerds(self, result: rm.Results, beerd_ids: list[ResultBeerds]) -> None:
""" """
Создать новый результат и сразу же вставить связанные `ResultBeerds`. Создать новый результат и сразу же вставить связанные `ResultBeerds`.
@ -74,7 +80,7 @@ class RecognizerRepository(ARecognizerRepository):
data_labels = f.read() data_labels = f.read()
return ujson.loads(data_labels) return ujson.loads(data_labels)
async def create_result_with_beerds(self, result: rm.Results, beerd_ids: list[str]) -> None: async def create_result_with_beerds(self, result: rm.Results, beerd_ids: list[ResultBeerds]) -> None:
""" """
Создаёт запись в ``recognizer_results`` и сразу же добавляет Создаёт запись в ``recognizer_results`` и сразу же добавляет
одну запись в ``recognizer_results_beerds`` (если передан список одну запись в ``recognizer_results_beerds`` (если передан список
@ -101,7 +107,8 @@ class RecognizerRepository(ARecognizerRepository):
{ {
"id": str(uuid4()), "id": str(uuid4()),
"recognizer_results_id": result.id, "recognizer_results_id": result.id,
"beerd_id": beerd_id, "beerd_id": beerd_id.beerd_id,
"probability": beerd_id.probability,
} }
for beerd_id in beerd_ids for beerd_id in beerd_ids
] ]

View File

@ -15,7 +15,7 @@ from torchvision import transforms # type: ignore
from server.modules.attachments.domains.attachments import Attachment from server.modules.attachments.domains.attachments import Attachment
from server.modules.descriptions.repository import ACharactersRepository, Breed from server.modules.descriptions.repository import ACharactersRepository, Breed
from server.modules.recognizer.repository import ARecognizerRepository, models from server.modules.recognizer.repository import ARecognizerRepository, ResultBeerds, models
TorchModel = NewType("TorchModel", torch.nn.Module) TorchModel = NewType("TorchModel", torch.nn.Module)
@ -48,11 +48,14 @@ class RecognizerResult(UJsonMixin):
description: dict[str, list] | None description: dict[str, list] | None
uploaded_attach_id: str | None uploaded_attach_id: str | None
@dataclass @dataclass
class SharingBeerds(UJsonMixin): class SharingBeerds(UJsonMixin):
alias: str alias: str
name: str name: str
images: list[ResultImages] images: list[str]
probability: float
@dataclass @dataclass
class SharingResult(UJsonMixin): class SharingResult(UJsonMixin):
@ -60,6 +63,12 @@ class SharingResult(UJsonMixin):
attachment_id: str attachment_id: str
@dataclass
class ResultNameBeerds:
name: str
probability: float
class RecognizerService: class RecognizerService:
__slots__ = ("_repository", "_attachment_service", "_repository_characters") __slots__ = ("_repository", "_attachment_service", "_repository_characters")
@ -79,7 +88,10 @@ class RecognizerService:
async def images_dogs(self) -> dict: async def images_dogs(self) -> dict:
return await self._repository.images_dogs() return await self._repository.images_dogs()
async def create_result(self, attachment: Attachment, user_id: str, device_id: str, beerd_names: list[str]): async def create_result(
self, attachment: Attachment, user_id: str, device_id: str, beerd_results: list[ResultNameBeerds]
):
beerd_names = {b.name: b for b in beerd_results}
characters = await self._repository_characters.get_characters() characters = await self._repository_characters.get_characters()
await self._repository.create_result_with_beerds( await self._repository.create_result_with_beerds(
models.Results( models.Results(
@ -89,7 +101,11 @@ class RecognizerService:
device_id=device_id, device_id=device_id,
created_at=datetime.now(UTC), created_at=datetime.now(UTC),
), ),
[ch.id for ch in characters if ch.name in beerd_names], [
ResultBeerds(beerd_id=ch.id, probability=beerd_names[ch.name].probability)
for ch in characters
if ch.name in beerd_names
],
) )
async def get_results(self, result_id: str) -> SharingResult: async def get_results(self, result_id: str) -> SharingResult:
@ -101,11 +117,15 @@ class RecognizerService:
continue continue
beers: list[SharingBeerds] = [] beers: list[SharingBeerds] = []
for beerd in r.beerds: for beerd in r.beerds:
beers.append(SharingBeerds( name = beerds_store[beerd.beerd_id].name.replace(" ", "_")
alias=beerds_store[beerd.beerd_id].alias, beers.append(
name=beerds_store[beerd.beerd_id].name, SharingBeerds(
images = [ResultImages(name=beerds_store[beerd.beerd_id].name, url=[f"/static/assets/cat/{beerds_store[beerd.beerd_id].name}/{i}" for i in images_dogs[beerds_store[beerd.beerd_id].name.replace(" ", "_")]])] alias=f"/dogs-characteristics/{beerds_store[beerd.beerd_id].alias}",
)) name=beerds_store[beerd.beerd_id].name,
probability=beerd.probability,
images=[f"/static/assets/dog/{name}/{i}" for i in images_dogs[name]],
)
)
return SharingResult(beerds=beers, attachment_id=r.result.attachment_id) return SharingResult(beerds=beers, attachment_id=r.result.attachment_id)
async def predict_dog_image(self, image: bytes, user_id: str, device_id: str | None) -> RecognizerResult: async def predict_dog_image(self, image: bytes, user_id: str, device_id: str | None) -> RecognizerResult:
@ -128,7 +148,14 @@ class RecognizerService:
) )
description.setdefault(name, []).append(f"/dogs-characteristics/{name.replace(' ', '_')}") description.setdefault(name, []).append(f"/dogs-characteristics/{name.replace(' ', '_')}")
results[probabilities] = name results[probabilities] = name
asyncio.create_task(self.create_result(attachment, user_id, device_id, [results[key] for key in results])) asyncio.create_task(
self.create_result(
attachment,
user_id,
device_id,
[ResultNameBeerds(name=results[key], probability=key*100) for key in results],
)
)
return RecognizerResult( return RecognizerResult(
results=results, images=images, description=description, uploaded_attach_id=attachment.id results=results, images=images, description=description, uploaded_attach_id=attachment.id
) )

View File

@ -1,26 +1,28 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block meta %} {% block meta %}
<meta name="description" content="Определение породы кошки по фото бесплатно и без регистраций. Определение породы происходит при помощи нейронной сети - точность опеределения составляет 70%." /> <meta name="description" content="Порода по фото - поделиться результатом" />
{% endblock %} {% endblock %}
{% block title %}Определение породы кошки по фото бесплатно{% endblock %} {% block title %}Результат определение породы по фото{% endblock %}
{% block content %} {% block content %}
<h1>Определить породу кошки по фото</h1> <h1>Мой результат определения породы по фото</h1>
<p>Загрузите фото, чтобы опеределить породу собаки или щенка. Если порода смешанная (или порода определена неточно), после загрузки будет показана вероятность породы животного.</p>
<p>Определение породы происходит при помощи нейронной сети - точность опеределения составляет 60%, сеть обучена на 65 породах. Если на фото будет неизвестная порода будет предложено несколько похожих пород.</p>
{% endblock %} {% endblock %}
{% block form %} {% block form %}
<div> <div>
<div id="upload-image">
<div id="upload-image-text">Ваше изображение:</div>
<img id="image" style="max-width: 200px;" src="/attachments{{ attachment.path }}.original.jpg">
</div>
<div id="result"> <div id="result">
<h3 class="image-results">Результаты</h3> <h3 class="image-results">Результаты</h3>
{% for result in result.beerds %} {% for result in result.beerds %}
<div class="image-block"><div class="image-text">{{ result.name }} (вероятность: {{ result.percent }}%) <br>{{ attachments[result.attachment_id].path }}<a href="{{ result.path }}" target="_blank">Описание и фото</a></div> <div class="image-block"><div class="image-text">{{ result.name }} (вероятность: {{ result.probability }}%) <br><a href="{{ result.alias }}" target="_blank">Описание и фото</a></div>
<div class="gallery-container"> <div class="gallery-container">
<div class="main-image-container"> <div class="main-image-container">
<img src="{{ result.images[0].path }}" class="main-image" data-urls="[&quot;/static/assets/dog/леонбергер/leonberger_140.webp&quot;,&quot;/static/assets/dog/леонбергер/leonberger_30.webp&quot;,&quot;/static/assets/dog/леонбергер/leonberger_47.webp&quot;,&quot;/static/assets/dog/леонбергер/leonberger_50.webp&quot;,&quot;/static/assets/dog/леонбергер/leonberger_82.webp&quot;]"></div> <img src="{{ result.images[0] }}" class="main-image"></div>
<div class="thumbnails" style="display:none;"> <div class="thumbnails" style="display:none;">
{% for image in result.images %} {% for image in result.images %}
<img src="{{ image.path }}" class="thumbnail"> <img src="{{ image }}" class="thumbnail">
{% endfor %} {% endfor %}
</div> </div>
</div> </div>