2025-05-24 14:35:35 +02:00

264 lines
15 KiB
HTML

{% extends "base.html" %}
{% block title %}Search Images - SEREACT Web Client{% endblock %}
{% block content %}
<div class="row justify-content-center mb-4">
<div class="col-md-10">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-search"></i> AI-Powered Image Search
</h5>
</div>
<div class="card-body">
<form method="POST">
<div class="row g-3">
<div class="col-md-8">
<label for="query" class="form-label">
<i class="fas fa-brain"></i> Search Query
</label>
<input type="text" class="form-control form-control-lg" id="query" name="query"
value="{{ query }}" placeholder="Describe what you're looking for..." required>
<div class="form-text">
Use natural language to describe the image content you're looking for
</div>
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary btn-lg w-100">
<i class="fas fa-search"></i> Search
</button>
</div>
</div>
<!-- Advanced Options -->
<div class="mt-3">
<button class="btn btn-outline-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#advancedOptions">
<i class="fas fa-cog"></i> Advanced Options
</button>
</div>
<div class="collapse mt-3" id="advancedOptions">
<div class="card bg-light">
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label for="limit" class="form-label">Results Limit</label>
<select class="form-select" id="limit" name="limit">
<option value="10" {{ 'selected' if request.form.get('limit') == '10' else '' }}>10 results</option>
<option value="20" {{ 'selected' if request.form.get('limit') == '20' else '' }}>20 results</option>
<option value="50" {{ 'selected' if request.form.get('limit') == '50' else '' }}>50 results</option>
</select>
</div>
<div class="col-md-4">
<label for="threshold" class="form-label">Similarity Threshold</label>
<select class="form-select" id="threshold" name="threshold">
<option value="0.5" {{ 'selected' if request.form.get('threshold') == '0.5' else '' }}>0.5 (More results)</option>
<option value="0.7" {{ 'selected' if request.form.get('threshold') == '0.7' or not request.form.get('threshold') else '' }}>0.7 (Balanced)</option>
<option value="0.8" {{ 'selected' if request.form.get('threshold') == '0.8' else '' }}>0.8 (More precise)</option>
<option value="0.9" {{ 'selected' if request.form.get('threshold') == '0.9' else '' }}>0.9 (Very precise)</option>
</select>
</div>
<div class="col-md-4">
<label for="tags" class="form-label">Filter by Tags</label>
<input type="text" class="form-control" id="tags" name="tags"
value="{{ request.form.get('tags', '') }}"
placeholder="tag1, tag2, tag3">
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% if query %}
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4>
<i class="fas fa-search-plus"></i> Search Results for "{{ query }}"
{% if total is defined %}
<span class="badge bg-primary">{{ total }} found</span>
{% endif %}
</h4>
<a href="{{ url_for('search') }}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Clear Search
</a>
</div>
</div>
</div>
{% if results %}
<div class="row">
{% for image in results %}
<div class="col-md-6 col-lg-4 col-xl-3 mb-4">
<div class="card h-100">
<div class="card-img-top bg-light d-flex align-items-center justify-content-center" style="height: 200px;">
<img src="{{ get_api_base_url() }}/api/v1/images/{{ image.id }}/download"
alt="{{ image.filename }}"
class="search-result-image"
onerror="this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMiIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkltYWdlIE5vdCBGb3VuZDwvdGV4dD48L3N2Zz4='">
</div>
<!-- Similarity Score Bar -->
{% if image.similarity_score %}
<div class="px-3 pt-2">
<div class="d-flex justify-content-between align-items-center mb-1">
<small class="text-muted">Similarity</small>
<small class="text-muted">{{ (image.similarity_score * 100) | round(1) }}%</small>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar" role="progressbar"
style="width: {{ (image.similarity_score * 100) | round(1) }}%;
background-color: {% if image.similarity_score > 0.8 %}#28a745{% elif image.similarity_score > 0.6 %}#ffc107{% else %}#dc3545{% endif %};">
</div>
</div>
</div>
{% endif %}
<div class="card-body">
<h6 class="card-title text-truncate" title="{{ image.filename }}">
{{ image.filename }}
</h6>
<p class="card-text small text-muted">
{{ image.description or 'No description' }}
</p>
<div class="small text-muted">
<div><i class="fas fa-weight"></i> {{ (image.file_size / 1024 / 1024) | round(2) }} MB</div>
<div><i class="fas fa-calendar"></i> {{ image.upload_date.strftime('%Y-%m-%d') if image.upload_date else 'Unknown' }}</div>
{% if image.tags %}
<div class="mt-2">
{% for tag in image.tags %}
<span class="badge bg-secondary me-1">{{ tag }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="card-footer bg-transparent">
<div class="btn-group w-100" role="group">
<a href="{{ url_for('view_image', image_id=image.id) }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye"></i> View
</a>
<a href="{{ get_api_base_url() }}/api/v1/images/{{ image.id }}/download"
class="btn btn-outline-secondary btn-sm" target="_blank">
<i class="fas fa-download"></i> Download
</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="fas fa-search fa-4x text-muted mb-3"></i>
<h4 class="text-muted">No Results Found</h4>
<p class="text-muted">Try adjusting your search query or reducing the similarity threshold.</p>
<div class="mt-3">
<button class="btn btn-outline-primary" onclick="document.getElementById('threshold').value='0.5'; document.querySelector('form').submit();">
<i class="fas fa-adjust"></i> Search with Lower Threshold
</button>
</div>
</div>
{% endif %}
{% else %}
<!-- Search Examples -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h6 class="mb-0">
<i class="fas fa-lightbulb"></i> Search Examples
</h6>
</div>
<div class="card-body">
<p class="text-muted mb-3">Try these example searches to see how AI-powered semantic search works:</p>
<div class="row">
<div class="col-md-6">
<h6><i class="fas fa-image text-primary"></i> Visual Content</h6>
<div class="d-flex flex-wrap gap-2 mb-3">
<button class="btn btn-outline-primary btn-sm" onclick="searchExample('sunset over mountains')">sunset over mountains</button>
<button class="btn btn-outline-primary btn-sm" onclick="searchExample('people smiling')">people smiling</button>
<button class="btn btn-outline-primary btn-sm" onclick="searchExample('red car')">red car</button>
<button class="btn btn-outline-primary btn-sm" onclick="searchExample('city skyline at night')">city skyline at night</button>
</div>
</div>
<div class="col-md-6">
<h6><i class="fas fa-palette text-success"></i> Colors & Moods</h6>
<div class="d-flex flex-wrap gap-2 mb-3">
<button class="btn btn-outline-success btn-sm" onclick="searchExample('bright colorful flowers')">bright colorful flowers</button>
<button class="btn btn-outline-success btn-sm" onclick="searchExample('dark moody atmosphere')">dark moody atmosphere</button>
<button class="btn btn-outline-success btn-sm" onclick="searchExample('blue ocean waves')">blue ocean waves</button>
<button class="btn btn-outline-success btn-sm" onclick="searchExample('warm golden light')">warm golden light</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h6><i class="fas fa-map-marker-alt text-info"></i> Places & Objects</h6>
<div class="d-flex flex-wrap gap-2 mb-3">
<button class="btn btn-outline-info btn-sm" onclick="searchExample('forest with tall trees')">forest with tall trees</button>
<button class="btn btn-outline-info btn-sm" onclick="searchExample('modern building architecture')">modern building architecture</button>
<button class="btn btn-outline-info btn-sm" onclick="searchExample('food on a plate')">food on a plate</button>
<button class="btn btn-outline-info btn-sm" onclick="searchExample('animal in nature')">animal in nature</button>
</div>
</div>
<div class="col-md-6">
<h6><i class="fas fa-running text-warning"></i> Activities & Emotions</h6>
<div class="d-flex flex-wrap gap-2 mb-3">
<button class="btn btn-outline-warning btn-sm" onclick="searchExample('people playing sports')">people playing sports</button>
<button class="btn btn-outline-warning btn-sm" onclick="searchExample('peaceful meditation')">peaceful meditation</button>
<button class="btn btn-outline-warning btn-sm" onclick="searchExample('celebration party')">celebration party</button>
<button class="btn btn-outline-warning btn-sm" onclick="searchExample('work meeting')">work meeting</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h6 class="mb-0">
<i class="fas fa-info-circle"></i> How AI Search Works
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<h6><i class="fas fa-brain text-primary"></i> Semantic Understanding</h6>
<p class="small">The AI understands the meaning behind your words, not just exact matches. It can find images based on concepts, emotions, and visual elements.</p>
</div>
<div class="col-md-4">
<h6><i class="fas fa-vector-square text-success"></i> Vector Similarity</h6>
<p class="small">Images are converted to high-dimensional vectors that capture visual features. Search finds images with similar vector representations.</p>
</div>
<div class="col-md-4">
<h6><i class="fas fa-sliders-h text-info"></i> Adjustable Precision</h6>
<p class="small">Use the similarity threshold to control how strict the matching is. Lower values return more results, higher values are more precise.</p>
</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
function searchExample(query) {
document.getElementById('query').value = query;
document.querySelector('form').submit();
}
</script>
{% endblock %}