Files
2025-01-12 06:15:15 +00:00

299 lines
12 KiB
Python

import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
import json
import re
from pymongo import MongoClient
import time
from bson import ObjectId
import os
import glob
from datetime import datetime, timedelta
# 数据库连接模块
class DatabaseHandler:
def __init__(self, mongo_uri, database_name, results_collection_name):
self.client = MongoClient(mongo_uri)
self.db = self.client[database_name]
self.results_collection = self.db[results_collection_name]
def save_result(self, result):
# 如果 result 中没有 filename,使用时间戳作为替代
# filename = result.get('filename', f"unknown_{result['timestamp']}")
filename = result.get('filename')
# 检查是否已存在相同 filename 的结果
existing_result = self.results_collection.find_one({'filename': filename})
if existing_result:
print(f"Video with filename {filename} has already been processed. Skipping.")
return
# 确保 result 中有 filename
result['filename'] = filename
# 将 ObjectId 转换为字符串
if 'video_id' in result and isinstance(result['video_id'], ObjectId):
result['video_id'] = str(result['video_id'])
self.results_collection.insert_one(result)
def is_sequence_processed(self, filename):
return self.results_collection.find_one({'filename': filename}) is not None
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ObjectId):
return str(o)
return super().default(o)
# 视频处理模块
class ImageSequenceProcessor:
def __init__(self, model_dir):
self.model = AutoModel.from_pretrained(model_dir, trust_remote_code=True,
attn_implementation='sdpa', torch_dtype=torch.bfloat16).eval().cuda()
self.tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)
self.max_size = 512 # 设置最大尺寸
def compress_image(self, image):
# 保持纵横比的情况下调整图片大小
image.thumbnail((self.max_size, self.max_size))
# 如果图像已经是JPEG格式,直接返回调整大小后的图像
if image.format == 'JPEG':
return image
# 对于非JPEG格式,进行压缩
buffer = io.BytesIO()
if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
# 保持透明度
image.save(buffer, format="PNG", optimize=True)
else:
# 转换为JPEG并压缩
image.convert('RGB').save(buffer, format="JPEG", quality=85, optimize=True)
buffer.seek(0)
return Image.open(buffer)
def process_image_sequence(self, image_paths):
frames = [self.compress_image(Image.open(img_path)) for img_path in image_paths]
question = "Analyze these 10 images as if they were frames from a video. Describe the scene in detail in Chinese, including the setting, number of people, their actions, and any changes or movements observed across the frames."
msgs = [
{'role': 'user', 'content': frames + [question]},
]
params = {
"use_image_id": False,
"max_slice_nums": 1
}
answer = self.model.chat(
image=None,
msgs=msgs,
tokenizer=self.tokenizer,
**params
)
extracted_info = self.extract_info(answer)
return {
"original_answer": answer,
"extracted_info": extracted_info,
"num_frames": len(frames),
}
@staticmethod
def extract_info(answer):
info = {
"environment": None,
"num_people": None,
"actions": [],
"interactions": [],
"objects": [],
"furniture": []
}
# 环境提取
environments = ["办公室", "室内", "室外", "会议室", "办公"]
for env in environments:
if env in answer.lower():
info["environment"] = env
break
# 改进的人数提取
people_patterns = [
r'(\d+)\s*(人|个人|位|名|员工|用户|小朋友|成年人|女性|男性)',
r'(一|二|三|四|五|六|七|八|九|十)\s*(人|个人|位|名|员工|用户|小朋友|成年人|女性|男性)',
r'(一个|几个)\s*(人|个人|员工|用户|小朋友|成年人|女性|男性)',
r'几\s*(名|位)\s*(人|员工|用户|小朋友|成年人|女性|男性)?',
r'(男|女)(性|生|士)',
r'(成年|未成年|青少年|老年)\s*(人|群体)',
r'(员工|职工|工人|学生|顾客|观众|游客|乘客)',
r'(群众|民众|大众|公众)',
r'(男女|老少|老幼|大人|小孩)'
]
for pattern in people_patterns:
match = re.search(pattern, answer)
if match:
if match.group(1).isdigit():
info["num_people"] = int(match.group(1))
elif match.group(1) in ['一个', '一']:
info["num_people"] = 1
else:
num_word_to_digit = {
'二': 2, '三': 3, '四': 4, '五': 5,
'六': 6, '七': 7, '八': 8, '九': 9, '十': 10
}
info["num_people"] = num_word_to_digit.get(match.group(1), 0)
break
# 动作和互动提取
actions = ["坐", "站", "摔倒", "跳舞", "转身", "摔", "倒", "倒下", "躺下", "转身", "跳跃", "跳", "躺", "睡", "说话"]
for action in actions:
if action in answer:
info["actions"].append(action)
interactions = ["互动", "交流", "身体语言", "交谈", "讨论", "开会"]
for interaction in interactions:
if interaction in answer:
info["interactions"].append(interaction)
# 物体和家具提取
objects = ["水瓶", "办公用品", "文件", "电脑"]
furniture = ["椅子", "桌子", "咖啡桌", "文件柜", "床", "沙发"]
for obj in objects:
if obj in answer:
info["objects"].append(obj)
for item in furniture:
if item in answer:
info["furniture"].append(item)
return info
# 主处理类
class ImageSequenceAnalysisSystem:
def __init__(self, mongo_uri, db_name, model_dir, results_collection_name):
self.db_handler = DatabaseHandler(mongo_uri, db_name, results_collection_name)
self.image_processor = ImageSequenceProcessor(model_dir)
self.last_processed_time = datetime.now() - timedelta(hours=1)
def get_all_images(self, image_folders):
image_files = []
for folder in image_folders:
image_files.extend(glob.glob(os.path.join(folder, '*.jpg')))
image_files.sort()
return image_files
def process_image_sequence(self, image_paths):
print(f"Attempting to process sequence: {[os.path.basename(img) for img in image_paths]}")
start_time = time.time()
try:
# 使用第一张图片的文件名作为序列的标识符
filename = os.path.basename(image_paths[0])
if self.db_handler.is_sequence_processed(filename):
print(f"Skipping already processed image sequence: {filename}")
return False
print("Processing new image sequence...")
result = self.image_processor.process_image_sequence(image_paths)
# timestamp = datetime.now()
# result['timestamp'] = timestamp.strftime("%Y%m%d_%H%M%S")
result['image_paths'] = image_paths
result['filename'] = filename
# 计算图片序列的周期
image_times = [self.get_file_time(img) for img in image_paths]
if len(image_times) >= 2:
time_diff = (image_times[-1] - image_times[0]).total_seconds()
period_minutes = time_diff / (len(image_times) - 1) / 60
result['sequence_period_minutes'] = round(period_minutes, 2)
# 添加时间段信息
result['time_range'] = {
'start': image_times[0].strftime("%Y-%m-%d %H:%M"),
'end': image_times[-1].strftime("%Y-%m-%d %H:%M")
}
save_result = self.db_handler.save_result(result)
print(f"Result saved to: {self.db_handler.results_collection.name}")
print(f"Result filename: {filename}")
return save_result
except Exception as e:
end_time = time.time()
processing_time = end_time - start_time
print(f"Error processing image sequence: {str(e)}")
print(f"Processing time (including error): {processing_time:.2f} seconds")
import traceback
traceback.print_exc()
return False
# @staticmethod
# def extract_time_from_filename(filename):
# # 假设文件名格式为 "YYYYMMDDHHMMSS.jpg"
# time_str = filename.split('.')[0]
# return datetime.strptime(time_str, "%Y%m%d%H%M")
@staticmethod
def get_file_time(file_path):
# 获取文件的修改时间
mod_time = os.path.getmtime(file_path)
return datetime.fromtimestamp(mod_time)
def process_all_unprocessed_images(self, image_folders):
print(f"Searching for unprocessed images in: {image_folders}")
all_images = self.get_all_images(image_folders)
print(f"Found {len(all_images)} images in total")
selected_images = all_images[::10]
# Group images into sequences of 3
image_sequences = [selected_images[i:i+10] for i in range(0, len(selected_images), 10)]
# image_sequences = [all_images[i:i+10] for i in range(0, len(all_images), 10)]
processed_sequences = 0
for sequence in image_sequences:
if len(sequence) == 10:
self.process_image_sequence(sequence)
processed_sequences += 1
else:
print(f"Warning: Incomplete sequence. Found {len(sequence)} images.")
if processed_sequences == 0:
print("All current photos have been processed. Waiting for new photos...")
else:
print(f"Processed {processed_sequences} sequences.")
def run(self, root_folders):
print(f"Starting the system with root folder: {', '.join(root_folders)}")
while True:
current_time = datetime.now()
time_since_last_process = (current_time - self.last_processed_time).total_seconds()
if time_since_last_process >= 3600: # 1小时 = 3600秒
self.process_all_unprocessed_images(root_folders)
self.last_processed_time = current_time
# 计算下次检查的等待时间
wait_time = max(0, 3600 - (datetime.now() - self.last_processed_time).total_seconds())
print(f"Waiting for new photos... Next check in {wait_time:.0f} seconds.")
time.sleep(60) # 每分钟检查一次是否需要处理
# 使用示例
if __name__ == "__main__":
mongo_uri = "mongodb://minio_mongo:[email protected]:27017/minio_mongo"
db_name = "minio_mongo"
results_collection_name = "cpm"
model_dir = "worker_sys/OpenBMB/MiniCPM-V-2_6"
root_folders = [
"/www/wwwroot/zj.obscura.ac.cn/ipcam/Office/Cam2/CapturePics" ,
"/www/wwwroot/zj.obscura.ac.cn/ipcam/Office/Cam1/CapturePics"
] # 修改为 cam1 文件夹的路径
system = ImageSequenceAnalysisSystem(mongo_uri, db_name, model_dir, results_collection_name)
system.run(root_folders)