识别任务完成更新任务状态为已结束;内存溢出问题优化;灾害数据输出excel面积长度和宽度计算公式错误

This commit is contained in:
liyubo 2025-12-18 14:02:54 +08:00
parent 6289a6d1e3
commit f349efaa56
4 changed files with 256 additions and 33 deletions

View File

@ -0,0 +1,109 @@
import psycopg2
from psycopg2.extras import RealDictCursor
import json
from typing import Dict, List, Union, Optional
from dataclasses import dataclass, asdict
from datetime import datetime
import re
@dataclass
class RecognitionTask:
"""识别任务"""
task_name: Optional[str] = None
model_id: Optional[int] = None
model_name: Optional[str] = None
model_version_id: Optional[str] = None
created_by: Optional[str] = None
status: Optional[int] = None
id: Optional[int] = None
exec_msg: Optional[str] = None
created_at: Optional[datetime] = None
result_url: Optional[str] = None
source_url: Optional[str] = None
task_id: Optional[str] = None
resource_record_id: Optional[int] = None
class RecognitionTaskDAO:
def __init__(self, db_params: Dict[str, str]):
"""
初始化数据库连接
参数:
db_params: 数据库连接参数包含:
- dbname: 数据库名
- user: 用户名
- password: 密码
- host: 主机地址
- port: 端口号
"""
self.db_params = db_params
def update_recognition_task(self, model: RecognitionTask) -> bool:
"""
更新现有的识别任务
参数:
model: 要更新的识别任务对象
返回:
是否更新成功
"""
if not isinstance(model, RecognitionTask):
raise ValueError("Invalid configuration type")
data = self._to_db_format(model)
query = """
UPDATE bz_recognition_tasks SET
status = %(status)s
WHERE task_id = %(task_id)s
"""
try:
with psycopg2.connect(**self.db_params) as conn:
with conn.cursor() as cur:
cur.execute(query, data)
conn.commit()
return True
except psycopg2.Error as e:
print(f"Database update error: {e}")
return False
def _to_db_format(self, task: RecognitionTask) -> Dict:
"""将RecognitionTask对象转换为数据库格式"""
return {
"id": task.id,
"task_name": task.task_name,
"model_id": task.model_id,
"model_name": task.model_name,
"model_version_id": task.model_version_id,
"status": task.status,
"exec_msg": task.exec_msg,
"created_at": task.created_at,
"created_by": task.created_by,
"result_url": task.result_url,
"source_url": task.source_url,
"task_id": task.task_id,
"resource_record_id": task.resource_record_id
}
def _from_db_format(self, db_data: Dict) -> RecognitionTask:
"""从数据库格式转换为RecognitionTask对象"""
return RecognitionTask(
id=db_data.get("id"),
task_name=db_data.get("task_name", ""),
model_id=db_data.get("model_id", 0),
model_name=db_data.get("model_name", ""),
model_version_id=db_data.get("model_version_id", ""),
status=db_data.get("status", 0),
exec_msg=db_data.get("exec_msg"),
created_at=db_data.get("created_at"),
created_by=db_data.get("created_by", ""),
result_url=db_data.get("result_url"),
source_url=db_data.get("source_url"),
task_id=db_data.get("task_id"),
resource_record_id=db_data.get("resource_record_id")
)

View File

@ -4,8 +4,9 @@ import zipfile
from os.path import exists from os.path import exists
import torch import torch
import gc
import os import os
import psutil
import cv2 import cv2
import numpy as np import numpy as np
import time import time
@ -106,6 +107,7 @@ class YOLOSegmentationInference:
self.device = device self.device = device
self.model = None self.model = None
self.class_names = [] self.class_names = []
self.clean_memory_per_count = 500
# 定义颜色映射(用于不同类别) # 定义颜色映射(用于不同类别)
self.colors = [ self.colors = [
@ -537,7 +539,7 @@ class YOLOSegmentationInference:
def process_single_image_share_dir(self, image_path, user_name, pwd, output_dir: Optional[str] = None, def process_single_image_share_dir(self, image_path, user_name, pwd, output_dir: Optional[str] = None,
conf_threshold: float = 0.25, iou_threshold: float = 0.5, conf_threshold: float = 0.25, iou_threshold: float = 0.5,
save_mask: bool = False, save_label: bool = False, show: bool = True, save_mask: bool = False, save_label: bool = False, show: bool = True,
result_save: [] = None) -> InferenceResult: result_save: [] = None) -> None:
""" """
处理单张图片 处理单张图片
@ -571,7 +573,7 @@ class YOLOSegmentationInference:
# if show: # if show:
# self.show_results(result) # self.show_results(result)
return result del result
def process_image_directory(self, input_dir: str, output_dir: Optional[str] = None, def process_image_directory(self, input_dir: str, output_dir: Optional[str] = None,
conf_threshold: float = 0.25, iou_threshold: float = 0.5, conf_threshold: float = 0.25, iou_threshold: float = 0.5,
@ -640,8 +642,7 @@ class YOLOSegmentationInference:
def process_image_directory_share_dir_circle(self, task_id, current_time, input_dir_list, user_name, pwd, output_dir: Optional[str] = None, def process_image_directory_share_dir_circle(self, task_id, current_time, input_dir_list, user_name, pwd, output_dir: Optional[str] = None,
conf_threshold: float = 0.25, iou_threshold: float = 0.5, conf_threshold: float = 0.25, iou_threshold: float = 0.5,
save_mask: bool = False, save_label: bool = False, show: bool = False, save_mask: bool = False, save_label: bool = False, show: bool = False,
result_save: [] = None) -> List[ result_save: [] = None) -> None:
InferenceResult]:
for input_dir in input_dir_list : for input_dir in input_dir_list :
self.process_image_directory_share_dir(task_id,current_time,input_dir,user_name,pwd,output_dir,conf_threshold,iou_threshold,save_mask,save_label,show,result_save) self.process_image_directory_share_dir(task_id,current_time,input_dir,user_name,pwd,output_dir,conf_threshold,iou_threshold,save_mask,save_label,show,result_save)
del_file_shutil(output_dir) del_file_shutil(output_dir)
@ -649,8 +650,7 @@ class YOLOSegmentationInference:
def process_image_directory_share_dir(self, task_id, current_time, input_dir, user_name, pwd, output_dir: Optional[str] = None, def process_image_directory_share_dir(self, task_id, current_time, input_dir, user_name, pwd, output_dir: Optional[str] = None,
conf_threshold: float = 0.25, iou_threshold: float = 0.5, conf_threshold: float = 0.25, iou_threshold: float = 0.5,
save_mask: bool = False, save_label: bool = False, show: bool = False, save_mask: bool = False, save_label: bool = False, show: bool = False,
result_save: [] = None) -> List[ result_save: [] = None) -> None:
InferenceResult]:
""" """
处理目录中的所有图片 处理目录中的所有图片
@ -666,7 +666,7 @@ class YOLOSegmentationInference:
Returns: Returns:
推理结果列表 推理结果列表
""" """
results = []
tmp_output_dir = output_dir + "\\" + datetime.now().strftime("%Y%m%d%H%M%S") tmp_output_dir = output_dir + "\\" + datetime.now().strftime("%Y%m%d%H%M%S")
print(f"正在处理共享目录: {input_dir} - {tmp_output_dir}") print(f"正在处理共享目录: {input_dir} - {tmp_output_dir}")
@ -676,19 +676,18 @@ class YOLOSegmentationInference:
scanner = get_scanner(zip_url=input_dir, user_name=user_name, pwd=pwd); scanner = get_scanner(zip_url=input_dir, user_name=user_name, pwd=pwd);
if not scanner.directory_exists(input_dir): if not scanner.directory_exists(input_dir):
print(f"错误: {input_dir} 不是有效的目录") print(f"错误: {input_dir} 不是有效的目录")
return results
# 获取所有图片文件 # 获取所有图片文件
image_files = scanner.get_smb_images(input_dir) image_files = scanner.get_smb_images(input_dir)
if not image_files: if not image_files:
print(f"在目录 {input_dir} 中未找到图片文件") print(f"在目录 {input_dir} 中未找到图片文件")
return results
print(f"找到 {len(image_files)} 个图片文件") print(f"找到 {len(image_files)} 个图片文件")
# 处理每张图片 # 处理每张图片
for image_path in image_files: for idx, image_path in enumerate(image_files):
# for image_path in image_files:
result = self.process_single_image_share_dir( result = self.process_single_image_share_dir(
image_path=image_path, image_path=image_path,
user_name=user_name, user_name=user_name,
@ -701,7 +700,21 @@ class YOLOSegmentationInference:
show=show, show=show,
result_save=result_save result_save=result_save
) )
results.append(result)
if idx % self.clean_memory_per_count == 0 :
print(f"idx = {idx}---每处理{self.clean_memory_per_count}张图片清理内存")
process = psutil.Process(os.getpid())
memory_info = process.memory_info()
mem_usage = memory_info.rss / 1024 / 1024 # 返回MB
print(f"清理前,内存使用: {mem_usage:.2f} MB")
if self.device == 'cuda':
torch.cuda.empty_cache()
gc.collect()
memory_info = process.memory_info()
mem_usage = memory_info.rss / 1024 / 1024 # 返回MB
print(f"清理后,内存使用: {mem_usage:.2f} MB")
# 推送识别数据到共享目录 # 推送识别数据到共享目录
tmpConfig = get_conf(input_dir, user_name, pwd) tmpConfig = get_conf(input_dir, user_name, pwd)
@ -728,14 +741,11 @@ class YOLOSegmentationInference:
status=2 status=2
) )
dao.update_recognition_task(recognition_task) dao.update_recognition_task(recognition_task)
return results
except PermissionError: except PermissionError:
print(f"权限错误: 无法访问目录 {input_dir}") print(f"权限错误: 无法访问目录 {input_dir}")
return results
except Exception as e: except Exception as e:
print(f"处理目录失败: {e}") print(f"处理目录失败: {e}")
return results
def predict_images(pt_name, zip_url, output_dir="predictions", conf_threshold=0.25, save_json=False): def predict_images(pt_name, zip_url, output_dir="predictions", conf_threshold=0.25, save_json=False):
zip_save_path = "dataset/zip_file" zip_save_path = "dataset/zip_file"

View File

@ -74,15 +74,51 @@ class SMBScanner:
print(f"读取Excel失败: {e}") print(f"读取Excel失败: {e}")
return None return None
def process_all_rows(self, df): def process_all_rows(self, df, columns=None):
""" """
处理所有行数据 处理所有行数据可选择指定列
Parameters:
-----------
df : pandas.DataFrame
要处理的数据框
columns : list or None, optional
要处理的列名列表None表示处理所有列
Returns:
--------
results : list
处理结果列表每个元素包含行信息和处理后的数据
""" """
if df is None or df.empty: if df is None or df.empty:
print("没有数据可处理") print("没有数据可处理")
return return []
print("开始处理每行数据:") # 验证和处理列参数
if columns is None:
# 使用所有列
selected_columns = df.columns.tolist()
else:
# 检查指定的列是否存在
valid_columns = []
invalid_columns = []
for col in columns:
if col in df.columns:
valid_columns.append(col)
else:
invalid_columns.append(col)
if invalid_columns:
print(f"警告:以下列名不存在,将被忽略: {invalid_columns}")
if not valid_columns:
print("错误:没有有效的列名可处理")
return []
selected_columns = valid_columns
print(f"开始处理数据,选择列: {selected_columns}")
print("=" * 60) print("=" * 60)
results = [] results = []
@ -91,17 +127,18 @@ class SMBScanner:
# print(f"\n处理第 {row_number} 行:") # print(f"\n处理第 {row_number} 行:")
# print("-" * 40) # print("-" * 40)
# 显示行数据 # 只显示选定的列数据
for col_name in df.columns: selected_data = {}
for col_name in selected_columns:
value = row[col_name] value = row[col_name]
selected_data[col_name] = value
# print(f" {col_name}: {value}") # print(f" {col_name}: {value}")
# 处理逻辑(根据实际需求修改) # 处理逻辑(根据实际需求修改)
processed_row = { processed_row = {
'row_number': row_number, 'row_number': row_number,
'original_index': index, 'original_index': index,
'data': row.to_dict(), 'data': selected_data # 只包含选定列的数据
'summary': f"处理了 {len(df.columns)} 个字段"
} }
results.append(processed_row) results.append(processed_row)
@ -111,7 +148,7 @@ class SMBScanner:
print(f"\n 进度: {row_number}/{len(df)} ({row_number/len(df)*100:.1f}%)") print(f"\n 进度: {row_number}/{len(df)} ({row_number/len(df)*100:.1f}%)")
# print("\n" + "=" * 60) # print("\n" + "=" * 60)
print(f"处理完成!共处理 {len(results)} 行数据") print(f"处理完成!共处理 {len(results)} 行数据{len(selected_columns)} 个字段")
return results return results
@ -767,6 +804,9 @@ def get_scanner(zip_url, user_name, pwd) :
def get_road_dict(dir,user_name,pwd) : def get_road_dict(dir,user_name,pwd) :
config = get_conf(dir, user_name, pwd) config = get_conf(dir, user_name, pwd)
scanner = get_scanner(dir, user_name=user_name, pwd=pwd) scanner = get_scanner(dir, user_name=user_name, pwd=pwd)
road_dict_for_width = get_road_dict_for_width(config, scanner)
found_paths = scanner.find_files_by_name( found_paths = scanner.find_files_by_name(
share_path=config['share'], share_path=config['share'],
file_name='每公里指标明细表*.xls', file_name='每公里指标明细表*.xls',
@ -780,7 +820,7 @@ def get_road_dict(dir,user_name,pwd) :
road_dict = {} road_dict = {}
if len(found_paths) > 0 : if len(found_paths) > 0 :
df = scanner.read_excel(found_paths[0]) df = scanner.read_excel(found_paths[0])
rows = scanner.process_all_rows(df) rows = scanner.process_all_rows(df, columns=['线路编码','区划代码','识别宽度(米)','方向(上行/下行)','技术等级','路面类型(沥青/水泥/砂石)','起桩号(米)','止桩号(米)'])
for i, row in enumerate(rows, 1): for i, row in enumerate(rows, 1):
data = row['data'] data = row['data']
if pd.notna(data['线路编码']) : if pd.notna(data['线路编码']) :
@ -788,6 +828,9 @@ def get_road_dict(dir,user_name,pwd) :
if data['方向(上行/下行)'] == '下行' : if data['方向(上行/下行)'] == '下行' :
up_or_down = 'B' up_or_down = 'B'
key = f"{data['线路编码']}{str(int(data['区划代码']))}{up_or_down}" key = f"{data['线路编码']}{str(int(data['区划代码']))}{up_or_down}"
width = road_dict_for_width.get(key)
if width :
data['识别宽度(米)'] = width
if road_dict.get(key) : if road_dict.get(key) :
road_dict[key].append(row) road_dict[key].append(row)
else : else :
@ -795,6 +838,29 @@ def get_road_dict(dir,user_name,pwd) :
return road_dict return road_dict
def get_road_dict_for_width(config, scanner):
found_paths_for_width = scanner.find_files_by_name(
share_path=config['share'],
file_name='nl_yy_glzb*.xls',
start_dir=config['dir'],
max_depth=4
)
road_dict_for_width = {}
if len(found_paths_for_width) > 0 :
df = scanner.read_excel(found_paths_for_width[0])
rows = scanner.process_all_rows(df, columns=['线路编码','区划代码','方向(上行/下行)','识别宽度(米)'])
for i, row in enumerate(rows, 1):
data = row['data']
if pd.notna(data['线路编码']) :
up_or_down = 'A'
if data['方向(上行/下行)'] == '下行' :
up_or_down = 'B'
key = f"{data['线路编码']}{str(int(data['区划代码']))}{up_or_down}"
width = data.get('识别宽度(米)')
if width :
road_dict_for_width[key] = width
return road_dict_for_width
# filename -> 桩号 # filename -> 桩号
def get_pile_dict(dir,user_name,pwd) : def get_pile_dict(dir,user_name,pwd) :
config = get_conf(dir, user_name, pwd) config = get_conf(dir, user_name, pwd)
@ -1030,8 +1096,42 @@ def main():
# flag = scanner.directory_exists(path) # flag = scanner.directory_exists(path)
# print(f"flag={flag}") # print(f"flag={flag}")
str = '\\\\192.168.110.114\\share_File\\西南计算机/北碚报送数据/3.25/图像类\\C071500109B\\Images_识别/77/20251120172316/77.zip' # str = '\\\\192.168.110.114\\share_File\\西南计算机/北碚报送数据/3.25/图像类\\C071500109B\\Images_识别/77/20251120172316/77.zip'
print(f"standardized_path(str)={standardized_path(str)}") # print(f"standardized_path(str)={standardized_path(str)}")
road_dict_for_width = {}
df = pd.read_excel('D:/devForBdzlWork/ai-train_platform/predictions/C005500155A/nl_yy_glzb (2)_识别宽度数据读取.xls')
rows = scanner.process_all_rows(df, columns=['线路编码','区划代码','方向(上行/下行)','识别宽度(米)'])
for i, row in enumerate(rows, 1):
data = row['data']
if pd.notna(data['线路编码']) :
up_or_down = 'A'
if data['方向(上行/下行)'] == '下行' :
up_or_down = 'B'
key = f"{data['线路编码']}{str(int(data['区划代码']))}{up_or_down}"
width = data.get('识别宽度(米)')
if width :
road_dict_for_width[key] = width
print('读取excel')
road_dict = {}
df = pd.read_excel('D:/devForBdzlWork/ai-train_platform/predictions/C005500155A/每公里指标明细表-农村公路技术状况评定结果表-500155.xls')
rows = scanner.process_all_rows(df, columns=['线路编码','区划代码','识别宽度(米)','方向(上行/下行)','技术等级','路面类型(沥青/水泥/砂石)','起桩号(米)','止桩号(米)'])
for i, row in enumerate(rows, 1):
data = row['data']
if pd.notna(data['线路编码']) :
up_or_down = 'A'
if data['方向(上行/下行)'] == '下行' :
up_or_down = 'B'
key = f"{data['线路编码']}{str(int(data['区划代码']))}{up_or_down}"
width = road_dict_for_width.get(key)
if width :
data['识别宽度(米)'] = width
if road_dict.get(key) :
road_dict[key].append(row)
else :
road_dict[key] = [row]
print('读取excel')
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -536,7 +536,7 @@ def format_number_to_k_code(number):
return f"K{integer_part:04d}+{decimal_part}" return f"K{integer_part:04d}+{decimal_part}"
# ---------------- 主函数-共享目录 ---------------- # ---------------- 主函数-共享目录 ----------------
def process_dir(road_dict,pile_dict,dir="output",cell_area=CELL_AREA,grid_width=GRID_WIDTH,grid_height=GRID_HEIGHT): def process_dir(road_dict,pile_dict,dir="output",cell_area=CELL_AREA,cell_width=CELL_WIDTH,cell_height=CELL_HEIGHT,grid_width=GRID_WIDTH,grid_height=GRID_HEIGHT):
os.makedirs(dir,exist_ok=True) os.makedirs(dir,exist_ok=True)
# 解压 # 解压
# 读取桩号映射 # 读取桩号映射
@ -556,7 +556,7 @@ def process_dir(road_dict,pile_dict,dir="output",cell_area=CELL_AREA,grid_width=
with open(grid_txt_path,'w',encoding='utf-8') as f: with open(grid_txt_path,'w',encoding='utf-8') as f:
f.write(out_txt) f.write(out_txt)
# 生成网格可视化 # 生成网格可视化
draw_grid_on_image(image_path,class_cells,cell_size=(GRID_WIDTH, GRID_HEIGHT),save_path=os.path.splitext(image_path)[0]+"_grid.jpg") # draw_grid_on_image(image_path,class_cells,cell_size=(GRID_WIDTH, GRID_HEIGHT),save_path=os.path.splitext(image_path)[0]+"_grid.jpg")
# 统计各类面积 # 统计各类面积
counts = {k:[len(v[0])*cell_area, v[1][0], v[1][1]] for k,v in class_cells.items()} counts = {k:[len(v[0])*cell_area, v[1][0], v[1][1]] for k,v in class_cells.items()}
# total_area = sum(counts.values()) # total_area = sum(counts.values())
@ -586,7 +586,7 @@ def process_dir(road_dict,pile_dict,dir="output",cell_area=CELL_AREA,grid_width=
process_damage_txt(road_dict, pile_dict, dir, summary_data, image_path, current_time) process_damage_txt(road_dict, pile_dict, dir, summary_data, image_path, current_time)
# 病害明细列表.xlsx # 病害明细列表.xlsx
img_file_path = process_damage_detail_excel(road_dict, pile_dict, dir, cell_area, summary_data, image_path) img_file_path = process_damage_detail_excel(road_dict, pile_dict, dir, cell_area, cell_width, cell_height, summary_data, image_path)
# 综合明细表.xlsx # 综合明细表.xlsx
process_damage_composite_excel(road_dict, pile_dict, summary_data, image_path, current_time, img_file_path) process_damage_composite_excel(road_dict, pile_dict, summary_data, image_path, current_time, img_file_path)
@ -650,7 +650,7 @@ def process_damage_detail_txt(road_dict, pile_dict, dir, summary_data, image_pat
print(f"输出完成: {out_file}") print(f"输出完成: {out_file}")
def process_damage_detail_excel(road_dict, pile_dict, dir, cell_area, summary_data, image_path): def process_damage_detail_excel(road_dict, pile_dict, dir, cell_area, cell_width, cell_height, summary_data, image_path):
print("输出:病害明细列表.xlsx") print("输出:病害明细列表.xlsx")
os.makedirs(f"{dir}/excel", exist_ok=True) os.makedirs(f"{dir}/excel", exist_ok=True)
headers = ['序号','路线编码','方向','桩号','路面类型','病害名称','程度','长度(m)',' 宽度(m)',' 面积(㎡)',' 横向位置','备注'] headers = ['序号','路线编码','方向','桩号','路面类型','病害名称','程度','长度(m)',' 宽度(m)',' 面积(㎡)',' 横向位置','备注']
@ -666,7 +666,7 @@ def process_damage_detail_excel(road_dict, pile_dict, dir, cell_area, summary_da
for data in summary_data: for data in summary_data:
damage_data = data[2] damage_data = data[2]
for attr_name, attr_value in damage_data.items(): for attr_name, attr_value in damage_data.items():
excel_data = [excel_index, road_code, up_or_down, f"K000{data[0]}", ROAD_TYPE_EN_TO_CN.get(road_type), attr_name, '', attr_value[1]*cell_area, attr_value[2]*cell_area, attr_value[0], '', ''] excel_data = [excel_index, road_code, up_or_down, f"K000{data[0]}", ROAD_TYPE_EN_TO_CN.get(road_type), attr_name, '', attr_value[1]*cell_width, attr_value[2]*cell_height, attr_value[0], '', '']
data_list.append(excel_data) data_list.append(excel_data)
all_data = [headers] + data_list all_data = [headers] + data_list
@ -961,10 +961,14 @@ if __name__=="__main__":
# calc_cell_area, calc_grid_width, calc_grid_height = calc_grid_param(2048, 4096, 3.6, 2) # calc_cell_area, calc_grid_width, calc_grid_height = calc_grid_param(2048, 4096, 3.6, 2)
# print(f"calc_cell_area={calc_cell_area}, calc_grid_width={calc_grid_width}, calc_grid_height={calc_grid_height}") # print(f"calc_cell_area={calc_cell_area}, calc_grid_width={calc_grid_width}, calc_grid_height={calc_grid_height}")
output_dir = "D:/devForBdzlWork/ai-train_platform/predictions/CV78500155B" output_dir = "D:/devForBdzlWork/ai-train_platform/predictions/C234500155A"
pile_dict = get_pile_dict(output_dir) pile_dict = get_pile_dict(output_dir)
road_dict = get_road_dict(output_dir) road_dict = get_road_dict(output_dir)
process_dir(road_dict, pile_dict, output_dir) process_dir(road_dict, pile_dict, output_dir)
# arr = [44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 68, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179]
# for a in arr :
# print(f"a = {a} x = {a % 37} y = {int(a / 37)}")