Spaces:
Runtime error
Runtime error
update
Browse files- app.py +91 -0
- config.py +13 -0
- data_visualization.py +54 -0
- database.py +44 -0
- feature_extraction.py +66 -0
- model.py +22 -0
- report_generation.py +31 -0
- requirements.txt +11 -0
- static/script.js +20 -0
- static/style.css +86 -0
- templates/history.html +18 -0
- templates/report.html +15 -0
- utils.py +24 -0
app.py
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import torch
|
3 |
+
from PIL import Image
|
4 |
+
import io
|
5 |
+
import pandas as pd
|
6 |
+
|
7 |
+
from model import RadarDetectionModel
|
8 |
+
from feature_extraction import (calculate_amplitude, classify_amplitude,
|
9 |
+
calculate_distribution_range, classify_distribution_range,
|
10 |
+
calculate_attenuation_rate, classify_attenuation_rate,
|
11 |
+
count_reflections, classify_reflections)
|
12 |
+
from report_generation import generate_report
|
13 |
+
from utils import plot_detection, render_report
|
14 |
+
from database import save_report, get_report_history
|
15 |
+
|
16 |
+
model = RadarDetectionModel()
|
17 |
+
|
18 |
+
|
19 |
+
def process_image(image):
|
20 |
+
detection_result = model.detect(image)
|
21 |
+
|
22 |
+
np_image = np.array(image)
|
23 |
+
amplitude = calculate_amplitude(np_image)
|
24 |
+
amplitude_class = classify_amplitude(amplitude)
|
25 |
+
|
26 |
+
box = detection_result['boxes'][0].tolist()
|
27 |
+
distribution_range = calculate_distribution_range(box)
|
28 |
+
distribution_class = classify_distribution_range(distribution_range)
|
29 |
+
|
30 |
+
attenuation_rate = calculate_attenuation_rate(np_image)
|
31 |
+
attenuation_class = classify_attenuation_rate(attenuation_rate)
|
32 |
+
|
33 |
+
reflection_count = count_reflections(np_image)
|
34 |
+
reflection_class = classify_reflections(reflection_count)
|
35 |
+
|
36 |
+
features = {
|
37 |
+
"振幅": amplitude_class,
|
38 |
+
"分布范围": distribution_class,
|
39 |
+
"衰减速度": attenuation_class,
|
40 |
+
"反射次数": reflection_class
|
41 |
+
}
|
42 |
+
|
43 |
+
report = generate_report(detection_result, image, features)
|
44 |
+
|
45 |
+
detection_image = plot_detection(image, detection_result)
|
46 |
+
|
47 |
+
save_report(report)
|
48 |
+
|
49 |
+
return detection_image, report
|
50 |
+
|
51 |
+
|
52 |
+
def analyze_radar_image(image):
|
53 |
+
detection_image, report = process_image(image)
|
54 |
+
report_html = render_report(report)
|
55 |
+
return detection_image, report_html
|
56 |
+
|
57 |
+
|
58 |
+
def display_history():
|
59 |
+
reports = get_report_history()
|
60 |
+
history_html = "<div class='history-container'><h3>历史记录</h3>"
|
61 |
+
for report in reports:
|
62 |
+
history_html += f"""
|
63 |
+
<div class='history-item'>
|
64 |
+
<p><strong>报告ID:</strong> {report.report_id}</p>
|
65 |
+
<p><strong>缺陷类型:</strong> {report.defect_type}</p>
|
66 |
+
<p><strong>描述:</strong> {report.description}</p>
|
67 |
+
<p><strong>创建时间:</strong> {report.created_at}</p>
|
68 |
+
</div>
|
69 |
+
"""
|
70 |
+
history_html += "</div>"
|
71 |
+
return history_html
|
72 |
+
|
73 |
+
|
74 |
+
with gr.Blocks(css="static/style.css") as iface:
|
75 |
+
gr.Markdown("# 雷达图谱分析系统")
|
76 |
+
with gr.Row():
|
77 |
+
with gr.Column(scale=1):
|
78 |
+
input_image = gr.Image(type="pil", label="上传雷达图谱")
|
79 |
+
analyze_button = gr.Button("分析")
|
80 |
+
with gr.Column(scale=2):
|
81 |
+
output_image = gr.Image(type="pil", label="检测结果")
|
82 |
+
output_report = gr.HTML(label="分析报告")
|
83 |
+
|
84 |
+
history_button = gr.Button("查看历史记录")
|
85 |
+
history_output = gr.HTML()
|
86 |
+
|
87 |
+
analyze_button.click(analyze_radar_image, inputs=[
|
88 |
+
input_image], outputs=[output_image, output_report])
|
89 |
+
history_button.click(display_history, inputs=[], outputs=[history_output])
|
90 |
+
|
91 |
+
iface.launch()
|
config.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
4 |
+
|
5 |
+
MODEL_NAME = "Extremely4606/paligemma_9_19"
|
6 |
+
DATABASE_URL = f"sqlite:///{os.path.join(BASE_DIR, 'radar_reports.db')}"
|
7 |
+
|
8 |
+
AMPLITUDE_THRESHOLD = 128
|
9 |
+
DISTRIBUTION_THRESHOLD = 1000
|
10 |
+
ATTENUATION_THRESHOLD = 0.5
|
11 |
+
REFLECTION_THRESHOLD = 2
|
12 |
+
|
13 |
+
HISTORY_LIMIT = 20
|
data_visualization.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import plotly.graph_objs as go
|
2 |
+
import pandas as pd
|
3 |
+
from sklearn.decomposition import PCA
|
4 |
+
|
5 |
+
|
6 |
+
def create_feature_radar_chart(features):
|
7 |
+
categories = ['振幅', '分布范围', '衰减速度', '反射次数']
|
8 |
+
values = [features[f'{cat}值'] for cat in categories]
|
9 |
+
|
10 |
+
fig = go.Figure(data=go.Scatterpolar(
|
11 |
+
r=values,
|
12 |
+
theta=categories,
|
13 |
+
fill='toself'
|
14 |
+
))
|
15 |
+
|
16 |
+
fig.update_layout(
|
17 |
+
polar=dict(
|
18 |
+
radialaxis=dict(visible=True, range=[0, max(values)])
|
19 |
+
),
|
20 |
+
showlegend=False
|
21 |
+
)
|
22 |
+
|
23 |
+
return fig.to_html(full_html=False)
|
24 |
+
|
25 |
+
|
26 |
+
def create_pca_visualization(reports):
|
27 |
+
features = ['振幅值', '分布范围值', '衰减速度值', '反射次数值']
|
28 |
+
X = pd.DataFrame([r.features for r in reports])[features]
|
29 |
+
|
30 |
+
pca = PCA(n_components=2)
|
31 |
+
pca_result = pca.fit_transform(X)
|
32 |
+
|
33 |
+
df = pd.DataFrame(data=pca_result, columns=['PC1', 'PC2'])
|
34 |
+
df['缺陷类型'] = [r.defect_type for r in reports]
|
35 |
+
|
36 |
+
fig = go.Figure(data=go.Scatter(
|
37 |
+
x=df['PC1'],
|
38 |
+
y=df['PC2'],
|
39 |
+
mode='markers',
|
40 |
+
marker=dict(
|
41 |
+
size=10,
|
42 |
+
color=df['缺陷类型'].map({'空洞': 'red', '裂缝': 'blue'}),
|
43 |
+
opacity=0.8
|
44 |
+
),
|
45 |
+
text=df['缺陷类型']
|
46 |
+
))
|
47 |
+
|
48 |
+
fig.update_layout(
|
49 |
+
title='PCA of Radar Features',
|
50 |
+
xaxis_title='Principal Component 1',
|
51 |
+
yaxis_title='Principal Component 2'
|
52 |
+
)
|
53 |
+
|
54 |
+
return fig.to_html(full_html=False)
|
database.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, JSON
|
2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
3 |
+
from sqlalchemy.orm import sessionmaker
|
4 |
+
from datetime import datetime
|
5 |
+
from config import DATABASE_URL, HISTORY_LIMIT
|
6 |
+
|
7 |
+
Base = declarative_base()
|
8 |
+
|
9 |
+
|
10 |
+
class Report(Base):
|
11 |
+
__tablename__ = 'reports'
|
12 |
+
|
13 |
+
id = Column(Integer, primary_key=True)
|
14 |
+
report_id = Column(String, unique=True)
|
15 |
+
defect_type = Column(String)
|
16 |
+
description = Column(String)
|
17 |
+
features = Column(JSON)
|
18 |
+
created_at = Column(DateTime, default=datetime.utcnow)
|
19 |
+
|
20 |
+
|
21 |
+
engine = create_engine(DATABASE_URL)
|
22 |
+
Base.metadata.create_all(engine)
|
23 |
+
Session = sessionmaker(bind=engine)
|
24 |
+
|
25 |
+
|
26 |
+
def save_report(report):
|
27 |
+
session = Session()
|
28 |
+
new_report = Report(
|
29 |
+
report_id=report['编号'],
|
30 |
+
defect_type=report['缺陷类型'],
|
31 |
+
description=report['缺陷描述'],
|
32 |
+
features=report['特征详情']
|
33 |
+
)
|
34 |
+
session.add(new_report)
|
35 |
+
session.commit()
|
36 |
+
session.close()
|
37 |
+
|
38 |
+
|
39 |
+
def get_report_history():
|
40 |
+
session = Session()
|
41 |
+
reports = session.query(Report).order_by(
|
42 |
+
Report.created_at.desc()).limit(HISTORY_LIMIT).all()
|
43 |
+
session.close()
|
44 |
+
return reports
|
feature_extraction.py
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
from scipy import signal
|
3 |
+
from config import AMPLITUDE_THRESHOLD, DISTRIBUTION_THRESHOLD, ATTENUATION_THRESHOLD, REFLECTION_THRESHOLD
|
4 |
+
|
5 |
+
|
6 |
+
def calculate_amplitude(image):
|
7 |
+
return np.max(image)
|
8 |
+
|
9 |
+
|
10 |
+
def classify_amplitude(amplitude):
|
11 |
+
return "强" if amplitude > AMPLITUDE_THRESHOLD else "弱"
|
12 |
+
|
13 |
+
|
14 |
+
def calculate_distribution_range(box):
|
15 |
+
width, height = box[2] - box[0], box[3] - box[1]
|
16 |
+
return width * height
|
17 |
+
|
18 |
+
|
19 |
+
def classify_distribution_range(area):
|
20 |
+
return "大" if area > DISTRIBUTION_THRESHOLD else "小"
|
21 |
+
|
22 |
+
|
23 |
+
def calculate_attenuation_rate(image):
|
24 |
+
gradient = np.gradient(np.array(image).mean(axis=2))
|
25 |
+
return np.mean(np.abs(gradient))
|
26 |
+
|
27 |
+
|
28 |
+
def classify_attenuation_rate(rate):
|
29 |
+
return "快" if rate > ATTENUATION_THRESHOLD else "慢"
|
30 |
+
|
31 |
+
|
32 |
+
def count_reflections(image, prominence=10):
|
33 |
+
gray = np.mean(np.array(image), axis=2)
|
34 |
+
peaks, _ = signal.find_peaks(np.mean(gray, axis=1), prominence=prominence)
|
35 |
+
return len(peaks)
|
36 |
+
|
37 |
+
|
38 |
+
def classify_reflections(count):
|
39 |
+
return "多次反射" if count >= REFLECTION_THRESHOLD else "单次反射"
|
40 |
+
|
41 |
+
|
42 |
+
def extract_features(image, detection_result):
|
43 |
+
np_image = np.array(image)
|
44 |
+
amplitude = calculate_amplitude(np_image)
|
45 |
+
amplitude_class = classify_amplitude(amplitude)
|
46 |
+
|
47 |
+
box = detection_result['boxes'][0].tolist()
|
48 |
+
distribution_range = calculate_distribution_range(box)
|
49 |
+
distribution_class = classify_distribution_range(distribution_range)
|
50 |
+
|
51 |
+
attenuation_rate = calculate_attenuation_rate(np_image)
|
52 |
+
attenuation_class = classify_attenuation_rate(attenuation_rate)
|
53 |
+
|
54 |
+
reflection_count = count_reflections(np_image)
|
55 |
+
reflection_class = classify_reflections(reflection_count)
|
56 |
+
|
57 |
+
return {
|
58 |
+
"振幅": amplitude_class,
|
59 |
+
"分布范围": distribution_class,
|
60 |
+
"衰减速度": attenuation_class,
|
61 |
+
"反射次数": reflection_class,
|
62 |
+
"振幅值": amplitude,
|
63 |
+
"分布范围值": distribution_range,
|
64 |
+
"衰减速度值": attenuation_rate,
|
65 |
+
"反射次数值": reflection_count
|
66 |
+
}
|
model.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoFeatureExtractor, AutoModelForObjectDetection
|
2 |
+
import torch
|
3 |
+
from config import MODEL_NAME
|
4 |
+
|
5 |
+
|
6 |
+
class RadarDetectionModel:
|
7 |
+
def __init__(self):
|
8 |
+
self.feature_extractor = AutoFeatureExtractor.from_pretrained(
|
9 |
+
MODEL_NAME)
|
10 |
+
self.model = AutoModelForObjectDetection.from_pretrained(MODEL_NAME)
|
11 |
+
self.model.eval()
|
12 |
+
|
13 |
+
@torch.no_grad()
|
14 |
+
def detect(self, image):
|
15 |
+
inputs = self.feature_extractor(images=image, return_tensors="pt")
|
16 |
+
outputs = self.model(**inputs)
|
17 |
+
|
18 |
+
target_sizes = torch.tensor([image.size[::-1]])
|
19 |
+
results = self.feature_extractor.post_process_object_detection(
|
20 |
+
outputs, threshold=0.5, target_sizes=target_sizes)[0]
|
21 |
+
|
22 |
+
return results
|
report_generation.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime
|
2 |
+
from jinja2 import Environment, FileSystemLoader
|
3 |
+
import os
|
4 |
+
|
5 |
+
env = Environment(loader=FileSystemLoader('templates'))
|
6 |
+
|
7 |
+
|
8 |
+
def generate_report(detection_result, image, features):
|
9 |
+
report_id = f"KD-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
10 |
+
|
11 |
+
defect_type = "空洞" if features['分布范围'] == "大" or features['反射次数'] == "多次反射" else "裂缝"
|
12 |
+
|
13 |
+
description = (f"{defect_type},振幅{features['振幅']},分布范围{features['分布范围']},"
|
14 |
+
f"衰减速度{features['衰减速度']},反射次数{features['反射次数']}")
|
15 |
+
|
16 |
+
report = {
|
17 |
+
"编号": report_id,
|
18 |
+
"缺陷类型": defect_type,
|
19 |
+
"测线位置": "拱顶", # 假设固定位置
|
20 |
+
"雷达图谱": image,
|
21 |
+
"缺陷描述": description,
|
22 |
+
"验证情况描述": "待验证",
|
23 |
+
"特征详情": features
|
24 |
+
}
|
25 |
+
|
26 |
+
return report
|
27 |
+
|
28 |
+
|
29 |
+
def render_report(report):
|
30 |
+
template = env.get_template('report.html')
|
31 |
+
return template.render(report=report)
|
requirements.txt
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio==3.37.0
|
2 |
+
torch==2.0.1
|
3 |
+
transformers==4.31.0
|
4 |
+
Pillow==9.5.0
|
5 |
+
numpy==1.23.5
|
6 |
+
matplotlib==3.7.1
|
7 |
+
pandas==1.5.3
|
8 |
+
sqlalchemy==2.0.19
|
9 |
+
plotly==5.15.0
|
10 |
+
scikit-learn==1.3.0
|
11 |
+
jinja2==3.1.2
|
static/script.js
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
document.addEventListener('DOMContentLoaded', function () {
|
2 |
+
// 添加交互性功能
|
3 |
+
const tabButtons = document.querySelectorAll('.tab-button')
|
4 |
+
const tabContents = document.querySelectorAll('.tab-content')
|
5 |
+
|
6 |
+
tabButtons.forEach((button) => {
|
7 |
+
button.addEventListener('click', () => {
|
8 |
+
const tabId = button.getAttribute('data-tab')
|
9 |
+
|
10 |
+
tabButtons.forEach((btn) => btn.classList.remove('active'))
|
11 |
+
tabContents.forEach((content) => content.classList.remove('active'))
|
12 |
+
|
13 |
+
button.classList.add('active')
|
14 |
+
document.getElementById(tabId).classList.add('active')
|
15 |
+
})
|
16 |
+
})
|
17 |
+
|
18 |
+
// 添加图表交互性(如果需要)
|
19 |
+
// 这里可以添加与 Plotly 图表交互的代码
|
20 |
+
})
|
static/style.css
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
font-family: Arial, sans-serif;
|
3 |
+
line-height: 1.6;
|
4 |
+
color: #333;
|
5 |
+
max-width: 1200px;
|
6 |
+
margin: 0 auto;
|
7 |
+
padding: 20px;
|
8 |
+
background-color: #f4f4f4;
|
9 |
+
}
|
10 |
+
|
11 |
+
.report-container {
|
12 |
+
background-color: #fff;
|
13 |
+
border-radius: 5px;
|
14 |
+
padding: 20px;
|
15 |
+
margin-bottom: 20px;
|
16 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
17 |
+
}
|
18 |
+
|
19 |
+
h2 {
|
20 |
+
color: #2c3e50;
|
21 |
+
border-bottom: 2px solid #2c3e50;
|
22 |
+
padding-bottom: 10px;
|
23 |
+
}
|
24 |
+
|
25 |
+
h3 {
|
26 |
+
color: #34495e;
|
27 |
+
}
|
28 |
+
|
29 |
+
ul {
|
30 |
+
list-style-type: none;
|
31 |
+
padding-left: 0;
|
32 |
+
}
|
33 |
+
|
34 |
+
li {
|
35 |
+
margin-bottom: 5px;
|
36 |
+
}
|
37 |
+
|
38 |
+
.detection-image {
|
39 |
+
max-width: 100%;
|
40 |
+
height: auto;
|
41 |
+
border-radius: 5px;
|
42 |
+
margin-bottom: 20px;
|
43 |
+
}
|
44 |
+
|
45 |
+
.history-container {
|
46 |
+
background-color: #fff;
|
47 |
+
border-radius: 5px;
|
48 |
+
padding: 20px;
|
49 |
+
margin-top: 20px;
|
50 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
51 |
+
}
|
52 |
+
|
53 |
+
.history-item {
|
54 |
+
border-bottom: 1px solid #eee;
|
55 |
+
padding: 10px 0;
|
56 |
+
}
|
57 |
+
|
58 |
+
.history-item:last-child {
|
59 |
+
border-bottom: none;
|
60 |
+
}
|
61 |
+
|
62 |
+
.feature-chart {
|
63 |
+
width: 100%;
|
64 |
+
height: 400px;
|
65 |
+
}
|
66 |
+
|
67 |
+
.tabs {
|
68 |
+
margin-bottom: 20px;
|
69 |
+
}
|
70 |
+
|
71 |
+
.tab-button {
|
72 |
+
background-color: #3498db;
|
73 |
+
color: white;
|
74 |
+
border: none;
|
75 |
+
padding: 10px 20px;
|
76 |
+
cursor: pointer;
|
77 |
+
transition: background-color 0.3s;
|
78 |
+
}
|
79 |
+
|
80 |
+
.tab-button:hover {
|
81 |
+
background-color: #2980b9;
|
82 |
+
}
|
83 |
+
|
84 |
+
.tab-button.active {
|
85 |
+
background-color: #2c3e50;
|
86 |
+
}
|
templates/history.html
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="history-container">
|
2 |
+
<h2>历史记录</h2>
|
3 |
+
{% for report in reports %}
|
4 |
+
<div class="history-item">
|
5 |
+
<p><strong>报告ID:</strong> {{ report.report_id }}</p>
|
6 |
+
<p><strong>缺陷类型:</strong> {{ report.defect_type }}</p>
|
7 |
+
<p><strong>描述:</strong> {{ report.description }}</p>
|
8 |
+
<p><strong>创建时间:</strong> {{ report.created_at }}</p>
|
9 |
+
<h3>特征详情:</h3>
|
10 |
+
<ul>
|
11 |
+
<li>振幅: {{ report.features.振幅 }} ({{ report.features.振幅值|round(2) }})</li>
|
12 |
+
<li>分布范围: {{ report.features.分布范围 }} ({{ report.features.分布范围值|round(2) }})</li>
|
13 |
+
<li>衰减速度: {{ report.features.衰减速度 }} ({{ report.features.衰减速度值|round(2) }})</li>
|
14 |
+
<li>反射次数: {{ report.features.反射次数 }} ({{ report.features.反射次数值 }})</li>
|
15 |
+
</ul>
|
16 |
+
</div>
|
17 |
+
{% endfor %}
|
18 |
+
</div>
|
templates/report.html
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="report-container">
|
2 |
+
<h2>雷达图谱分析报告</h2>
|
3 |
+
<p><strong>编号:</strong> {{ report.编号 }}</p>
|
4 |
+
<p><strong>缺陷类型:</strong> {{ report.缺陷类型 }}</p>
|
5 |
+
<p><strong>测线位置:</strong> {{ report.测线位置 }}</p>
|
6 |
+
<p><strong>缺陷描述:</strong> {{ report.缺陷描述 }}</p>
|
7 |
+
<p><strong>验证情况描述:</strong> {{ report.验证情况描述 }}</p>
|
8 |
+
<h3>特征详情:</h3>
|
9 |
+
<ul>
|
10 |
+
<li>振幅: {{ report.特征详情.振幅 }} ({{ report.特征详情.振幅值|round(2) }})</li>
|
11 |
+
<li>分布范围: {{ report.特征详情.分布范围 }} ({{ report.特征详情.分布范围值|round(2) }})</li>
|
12 |
+
<li>衰减速度: {{ report.特征详情.衰减速度 }} ({{ report.特征详情.衰减速度值|round(2) }})</li>
|
13 |
+
<li>反射次数: {{ report.特征详情.反射次数 }} ({{ report.特征详情.反射次数值}})</li>
|
14 |
+
</ul>
|
15 |
+
</div>
|
utils.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import matplotlib.pyplot as plt
|
2 |
+
import io
|
3 |
+
import base64
|
4 |
+
|
5 |
+
|
6 |
+
def plot_detection(image, detection_result):
|
7 |
+
plt.figure(figsize=(10, 10))
|
8 |
+
plt.imshow(image)
|
9 |
+
ax = plt.gca()
|
10 |
+
|
11 |
+
for score, label, box in zip(detection_result["scores"], detection_result["labels"], detection_result["boxes"]):
|
12 |
+
x, y, w, h = box
|
13 |
+
rect = plt.Rectangle((x, y), w-x, h-y, fill=False, color='red')
|
14 |
+
ax.add_patch(rect)
|
15 |
+
ax.text(x, y, f'{label}: {score:.2f}',
|
16 |
+
bbox=dict(facecolor='white', alpha=0.8))
|
17 |
+
|
18 |
+
plt.axis('off')
|
19 |
+
|
20 |
+
buf = io.BytesIO()
|
21 |
+
plt.savefig(buf, format='png')
|
22 |
+
buf.seek(0)
|
23 |
+
img_str = base64.b64encode(buf.getvalue()).decode()
|
24 |
+
return f"data:image/png;base64,{img_str}"
|