人工智能辅助荧光浓度检测系统:基于YOLO与RGB分析的Python实现
本文介绍了一种基于YOLO目标检测和RGB分析的荧光浓度检测Python系统。系统通过YOLOv8模型定位荧光区域,提取RGB值并转换为浓度值。核心实现包括:1)使用ultralytics库加载训练好的YOLO模型进行目标检测;2)从检测区域提取RGB特征;3)应用预定义的校准模型计算浓度。该系统解决了图像质量、精确定位和颜色提取等技术挑战,可集成到Web应用中。文中提供了详细的代码实现,包括模型
人工智能辅助荧光浓度检测系统:基于YOLO与RGB分析的Python实现
第一部分:项目概述与系统架构
1.1 任务核心与挑战
任务核心:开发一个Python程序,能够接收用户上传的荧光图像,利用YOLO(You Only Look Once)目标检测模型定位图像中的特定荧光区域,提取该区域的RGB颜色值,并通过预先建立的数学模型(RGB比值与浓度之间的关系)计算出未知样本的浓度。
技术挑战:
- 图像质量:用户上传的图片可能存在光照不均、背景干扰、白平衡不准、格式不一等问题。
- 精确定位:需要准确识别并定位出图片中承载荧光物质的区域(如微孔板中的孔、试管、试纸条等),排除无关背景。
- 颜色提取:从定位到的区域中提取出有代表性的RGB值,需要考虑是取平均值、中值还是特定区域的值。
- 模型建立:RGB值与浓度之间的关系(校准曲线)并非简单的线性关系,可能需要多项式、指数或对数模型来拟合。
- 系统集成:将计算机视觉(YOLO)、图像处理(OpenCV)、数据建模(Scikit-learn)和Web交互(Flask/Django)等技术无缝集成。
1.2 系统架构设计
一个完整的系统应包含以下模块:
- 用户交互层(Web前端):允许用户上传图片、查看检测结果和历史记录。
- 业务逻辑层(Python后端):处理HTTP请求,协调各个模块的工作流程。
- 视觉处理层(YOLO + OpenCV):加载YOLO模型,执行目标检测,并提取ROI(Region of Interest)。
- 颜色分析层(OpenCV + NumPy):处理ROI,计算其RGB统计特征(均值、标准差等)。
- 浓度计算层(Scikit-learn / SciPy):应用预定义的校准模型,将RGB值或其衍生特征转换为浓度值。
- 数据持久层(数据库):存储用户上传的图片、分析结果和校准模型数据。
用户 -> [Web前端] -> [Python后端] -> [YOLO模型] -> [RGB提取] -> [浓度计算] -> [数据库]
结果展示 <- <- <- <- <-
本文将深度聚焦于业务逻辑层、视觉处理层、颜色分析层和浓度计算层的核心实现,并提供关于其他层的实现思路。
第二部分:环境配置与依赖库
在开始编写代码之前,需要配置一个强大的Python环境。推荐使用conda
或venv
创建虚拟环境。
2.1 所需库及其作用
# 核心计算机视觉库
pip install opencv-python # OpenCV核心库,用于图像处理
pip install opencv-python-headless # 无GUI支持的OpenCV,适用于服务器部署
pip install ultralytics # 一个非常流行且易用的YOLOv8封装库,强烈推荐
# 科学计算与数据处理
pip install numpy # 数组计算的核心库
pip install scipy # 科学计算,用于曲线拟合和优化
pip install pandas # 数据处理和分析
pip install scikit-learn # 机器学习,用于建立回归模型
# Web框架(可选,用于构建完整应用)
pip install flask # 轻量级Web框架
pip install django # 重量级全功能Web框架
# 图像处理辅助
pip install pillow # Python图像处理库(PIL的分支),常用于处理上传的图片
pip install matplotlib # 绘图库,用于结果可视化和调试
# 其他工具
pip install tqdm # 进度条工具
2.2 安装说明
对于ultralytics
库,它简化了YOLO模型的下载、训练和推理过程。如果您需要使用官方Darknet版本的YOLO,过程会更为复杂,需要从源码编译OpenCV和Darknet。本文以ultralytics
(YOLOv8) 为例,因为它的易用性和高性能。
第三部分:YOLO模型准备与推理
3.1 模型选择与准备
YOLO模型需要先被训练来识别您的特定荧光容器(如96孔板、试管、检测线等)。
- 数据收集:收集数百张包含目标容器(如微孔板)的图片,在不同光照、角度下拍摄。
- 数据标注:使用标注工具(如LabelImg、CVAT、Roboflow)框出目标区域,并赋予标签(例如
well
)。 - 模型训练:
- 如果您使用
ultralytics
YOLOv8,训练命令非常简单:yolo detect train data=your_dataset.yaml model=yolov8n.pt epochs=100
- 训练完成后,会得到一个
best.pt
权重文件。
- 如果您使用
- 模型放置:将训练好的
best.pt
模型文件放在项目目录中,例如./models/fluorescence_detector.pt
。
注意:如果您的项目只是一个demo或者没有条件进行训练,可以使用一个预训练的COCO模型,它可能能检测出一些通用的“瓶瓶罐罐”(如bottle
, cup
),但精度会远低于专用模型。本文假设您已有一个训练好的自定义模型。
3.2 实现YOLO推理与ROI提取
以下是使用ultralytics
的YOLOv8进行推理和ROI提取的核心代码。
import cv2
import numpy as np
from ultralytics import YOLO
from PIL import Image
import io
class FluorescenceDetector:
def __init__(self, model_path):
"""
初始化荧光检测器
:param model_path: 训练好的YOLO模型(.pt文件)路径
"""
# 加载训练好的YOLOv8模型
self.model = YOLO(model_path)
# 定义类别名称(应与训练时一致)
self.class_names = ['well'] # 例如:['well', 'test_strip']
print(f"模型从 {model_path} 加载成功。")
def predict_and_crop(self, image_source, confidence_threshold=0.7):
"""
对输入图像进行预测,并返回裁剪出的目标区域(ROI)
:param image_source: 图像源,可以是文件路径、numpy数组或PIL图像
:param confidence_threshold: 置信度阈值,高于此值才被认为是有效检测
:return: list 返回一个列表,每个元素是一个字典,包含ROI图像和其边界框信息
"""
# 使用模型进行预测
results = self.model(image_source, conf=confidence_threshold)
detected_rois = []
# 遍历每个预测结果(如果批量处理,results会有多个元素)
for result in results:
# 如果检测到了目标
if result.boxes is not None and len(result.boxes) > 0:
# 获取原始图像
orig_img = result.orig_img
# 获取边界框坐标、置信度和类别ID
boxes = result.boxes.xyxy.cpu().numpy() # 边界框 [x1, y1, x2, y2]
confidences = result.boxes.conf.cpu().numpy()
class_ids = result.boxes.cls.cpu().numpy().astype(int)
for i, box in enumerate(boxes):
confidence = confidences[i]
class_id = class_ids[i]
class_name = self.class_names[class_id]
# 确保坐标是整数
x1, y1, x2, y2 = map(int, box)
# 从原图中裁剪出ROI
roi = orig_img[y1:y2, x1:x2]
# 将ROI信息添加到列表
detected_rois.append({
'roi_image': roi,
'bbox': (x1, y1, x2, y2),
'confidence': confidence,
'class_name': class_name
})
print(f"检测到 {class_name},置信度: {confidence:.2f},位置: ({x1}, {y1}, {x2}, {y2})")
else:
print("未检测到任何目标。")
# 在这里可以返回整个图像或None,或者抛出异常,取决于您的业务逻辑
# 例如:detected_rois.append({'roi_image': orig_img, 'bbox': None, ...})
return detected_rois
# 示例用法
if __name__ == '__main__':
# 初始化检测器
detector = FluorescenceDetector('./models/fluorescence_detector.pt')
# 从文件读取图像
image_path = 'user_uploaded_image.jpg'
# 进行预测和裁剪
rois = detector.predict_and_crop(image_path)
# 显示或保存裁剪出的ROI
for i, roi_info in enumerate(rois):
roi_img = roi_info['roi_image']
cv2.imwrite(f'roi_{i}.jpg', roi_img)
# 或者用matplotlib显示
# plt.imshow(cv2.cvtColor(roi_img, cv2.COLOR_BGR2RGB))
# plt.show()
关键点说明:
YOLO(model_path)
:ultralytics
库使模型加载变得极其简单。results = self.model(image_source)
:执行推理。image_source
可以是路径、numpy数组、PIL图像、URL等,非常灵活。result.boxes
:包含所有检测框的信息。result.orig_img
:获取原始的OpenCV格式(BGR)图像。- 我们遍历每个检测到的框,提取其坐标,并从原图中裁剪出对应的区域(ROI)。
第四部分:RGB值提取与预处理
从YOLO提取的ROI可能仍然包含一些我们不想要的边缘区域。我们需要进一步处理ROI以获取最纯净的颜色代表。
4.1 ROI后处理与颜色提取策略
def extract_dominant_rgb(self, roi_image, method='mean', mask_percentage=0.8):
"""
从ROI图像中提取主导RGB值
:param roi_image: 裁剪出的ROI图像 (numpy数组, BGR格式)
:param method: 提取方法,可选 'mean'(平均值), 'median'(中值), 'max_area'(最大连通域)
:param mask_percentage: 当method为‘max_area’时,用于创建中心掩码的百分比
:return: 返回一个包含R, G, B值的元组 (R, G, B)
"""
# 确保图像是彩色图
if len(roi_image.shape) == 3:
# 将BGR转换为RGB(因为OpenCV是BGR,但通常我们思维是RGB)
roi_rgb = cv2.cvtColor(roi_image, cv2.COLOR_BGR2RGB)
else:
raise ValueError("输入图像必须是彩色的。")
if method == 'mean':
# 计算整个ROI的平均RGB值
r_mean = np.mean(roi_rgb[:, :, 0])
g_mean = np.mean(roi_rgb[:, :, 1])
b_mean = np.mean(roi_rgb[:, :, 2])
return (r_mean, g_mean, b_mean)
elif method == 'median':
# 计算整个ROI的RGB中值,对异常值更鲁棒
r_median = np.median(roi_rgb[:, :, 0])
g_median = np.median(roi_rgb[:, :, 1])
b_median = np.median(roi_rgb[:, :, 2])
return (r_median, g_median, b_median)
elif method == 'max_area':
# 一种更高级的方法:聚焦于ROI的中心区域,避免边缘效应的干扰
height, width = roi_rgb.shape[:2]
# 创建一个中心区域的掩码
mask = np.zeros((height, width), dtype=np.uint8)
center_x, center_y = width // 2, height // 2
# 计算中心矩形的尺寸(例如,ROI大小的80%)
rect_width = int(width * mask_percentage)
rect_height = int(height * mask_percentage)
x1 = center_x - rect_width // 2
y1 = center_y - rect_height // 2
x2 = center_x + rect_width // 2
y2 = center_y + rect_height // 2
# 确保坐标不越界
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(width, x2), min(height, y2)
# 在掩码上绘制白色矩形
cv2.rectangle(mask, (x1, y1), (x2, y2), 255, -1)
# 使用掩码计算中心区域的平均RGB
mean_val = cv2.mean(roi_rgb, mask=mask)
# mean_val 返回的是 [R, G, B, A] 格式,A是alpha通道,这里没有所以是0
return (mean_val[0], mean_val[1], mean_val[2])
else:
raise ValueError(f"不支持的提取方法: {method}。请使用 'mean', 'median', 或 'max_area'。")
def calculate_rgb_ratios(self, rgb_tuple):
"""
计算常见的RGB比值
:param rgb_tuple: 包含R, G, B值的元组
:return: 字典,包含各种比值
"""
r, g, b = rgb_tuple
total = r + g + b
# 避免除以零
if total == 0:
return {'r/g': 0, 'r/b': 0, 'g/b': 0, 'r_norm': 0, 'g_norm': 0, 'b_norm': 0}
r_norm = r / total
g_norm = g / total
b_norm = b / total
ratios = {
'r/g': r / g if g != 0 else 0,
'r/b': r / b if b != 0 else 0,
'g/b': g / b if b != 0 else 0,
'r_norm': r_norm,
'g_norm': g_norm,
'b_norm': b_norm,
# 有时单一通道的强度或对数强度更有效
'r_intensity': r,
'g_intensity': g,
'b_intensity': b,
}
return ratios
4.2 图像预处理以增强鲁棒性
用户上传的图像可能不理想。在提取RGB之前,可以进行一些预处理。
def preprocess_image(self, image):
"""
对图像进行预处理以提高颜色提取的稳定性
:param image: 输入图像 (BGR格式)
:return: 预处理后的图像
"""
# 1. 高斯模糊:减少噪声影响
blurred = cv2.GaussianBlur(image, (5, 5), 0)
# 2. 直方图均衡化(在HSV或LAB空间下效果更好)
# 转换到LAB颜色空间
lab = cv2.cvtColor(blurred, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
# 对L通道进行CLAHE均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl = clahe.apply(l)
# 合并通道并转换回BGR
limg = cv2.merge((cl, a, b))
processed = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
# 也可以尝试简单的Gamma校正来调整亮度
# gamma = 1.5
# inv_gamma = 1.0 / gamma
# table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8")
# processed = cv2.LUT(processed, table)
return processed
# 在 predict_and_crop 方法中,可以在预测前先预处理整个原图
# orig_img = self.preprocess_image(orig_img)
# 或者在提取ROI的RGB值前预处理单个ROI
# processed_roi = self.preprocess_image(roi)
# rgb_values = self.extract_dominant_rgb(processed_roi, method='max_area')
关键点说明:
- 提取方法:
mean
简单快速,median
抗噪声,max_area
能有效排除容器边缘、反光等干扰,通常是最佳选择。 - 颜色空间:OpenCV默认使用BGR,但我们通常用RGB来思考,所以需要转换。
- 比值计算:归一化的RGB值(
r_norm
,g_norm
,b_norm
)或通道比值(r/g
)通常比原始绝对值对光照变化更具鲁棒性,更适合作为浓度模型的输入特征。 - 预处理:CLAHE(对比度受限的自适应直方图均衡化)在LAB空间处理可以有效地改善光照不均问题,而不引入太多颜色失真。
第五部分:建立浓度校准模型
这是系统的核心“大脑”,它将RGB特征映射到浓度值。
5.1 模型选择与校准数据
浓度与颜色关系通常是非线性的。常见的模型有:
- 多项式回归:
浓度 = a * (R/G) + b * (R/G)^2 + c
- 指数/对数模型:
浓度 = a * exp(b * R_intensity) + c
或浓度 = a * log(R_intensity) + b
- S型函数(Sigmoid):适用于有上下限的检测,如
浓度 = L / (1 + exp(-k * (R_norm - x0)))
- 机器学习模型:对于非常复杂的关系,可以使用随机森林或梯度提升树(如XGBoost),它们能自动学习特征交互和非线性关系。
您需要先进行校准实验:
- 准备一系列已知浓度的标准样本。
- 在标准化的条件下(光照、相机参数、容器)拍摄它们的图片。
- 用上面的YOLO和RGB提取程序处理这些图片,为每个已知浓度样本得到一组RGB特征(例如
r_norm
,g_norm
,r/g
)。 - 这样就得到了一个数据集:
(浓度, 特征1, 特征2, ...)
。
5.2 实现模型拟合与预测
假设我们使用多项式回归,并用scipy.optimize.curve_fit
进行拟合。
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import joblib # 用于保存和加载模型
class ConcentrationModel:
def __init__(self):
self.model_params = None
self.model_type = None # 'poly', 'exp', 'sigmoid', 'rf'
self.feature_name = None # 使用的特征,例如 'r_norm'
def create_calibration_data(self):
"""
创建或加载校准数据。
这里应该是从数据库或CSV文件读取的事先准备好的数据。
返回格式: (concentrations, features)
例如: concentrations = [0.1, 0.5, 1.0, 2.0, 5.0]
features = [0.2, 0.35, 0.45, 0.6, 0.75] # 对应浓度的 r_norm 值
"""
# 这里用虚拟数据演示
# 在实际应用中,这应该从文件或数据库读取
self.known_concentrations = np.array([0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0])
self.known_features = np.array([0.15, 0.25, 0.4, 0.5, 0.65, 0.8, 0.9]) # 假设是r_norm
return self.known_concentrations, self.known_features
def poly_model(self, x, a, b, c):
"""二次多项式模型"""
return a * x**2 + b * x + c
def exp_model(self, x, a, b, c):
"""指数模型"""
return a * np.exp(b * x) + c
def sigmoid_model(self, x, L, k, x0):
"""S型函数模型"""
return L / (1 + np.exp(-k * (x - x0)))
def fit_polynomial(self, degree=2):
"""使用多项式拟合校准数据"""
conc, feat = self.create_calibration_data()
# 使用numpy的polyfit进行多项式拟合
self.model_params = np.polyfit(feat, conc, degree)
self.model_type = 'poly'
self.feature_name = 'r_norm'
print(f"多项式拟合参数: {self.model_params}")
# 计算R²
p = np.poly1d(self.model_params)
y_pred = p(feat)
r2 = r2_score(conc, y_pred)
print(f"多项式拟合 R² 分数: {r2:.4f}")
return r2
def fit_curve(self, model_func):
"""使用scipy的curve_fit进行非线性曲线拟合"""
conc, feat = self.create_calibration_data()
try:
params, _ = curve_fit(model_func, feat, conc, maxfev=5000)
self.model_params = params
print(f"曲线拟合参数: {params}")
# 计算R²
y_pred = model_func(feat, *params)
r2 = r2_score(conc, y_pred)
print(f"曲线拟合 R² 分数: {r2:.4f}")
return r2
except RuntimeError as e:
print(f"曲线拟合错误: {e}")
return None
def fit_random_forest(self, feature_data, target_data):
"""
使用随机森林进行拟合
:param feature_data: 二维数组,每行是一个样本的所有特征,例如 [[r_norm, g_norm, r/g], ...]
:param target_data: 一维数组,浓度值
"""
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(feature_data, target_data, test_size=0.2, random_state=42)
self.model = RandomForestRegressor(n_estimators=100, random_state=42)
self.model.fit(X_train, y_train)
self.model_type = 'rf'
# 评估模型
y_pred = self.model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"随机森林 MSE: {mse:.4f}, R²: {r2:.4f}")
# 特征重要性
if hasattr(self.model, 'feature_importances_'):
print("特征重要性:", dict(zip(['feat1', 'feat2', ...], self.model.feature_importances_)))
return r2
def predict_concentration(self, feature_value):
"""
根据拟合好的模型预测浓度
:param feature_value: 输入的特征值。对于多项式/曲线是单个值,对于RF是特征列表
:return: 预测的浓度值
"""
if self.model_type == 'poly':
p = np.poly1d(self.model_params)
return p(feature_value)
elif self.model_type == 'exp':
return self.exp_model(feature_value, *self.model_params)
elif self.model_type == 'sigmoid':
return self.sigmoid_model(feature_value, *self.model_params)
elif self.model_type == 'rf':
# 确保feature_value是二维数组
return self.model.predict([feature_value])[0]
else:
raise ValueError("模型尚未训练或类型未知。")
def plot_calibration_curve(self, save_path=None):
"""绘制校准曲线,用于可视化验证"""
conc, feat = self.create_calibration_data()
plt.figure(figsize=(10, 6))
plt.scatter(feat, conc, color='blue', label='校准数据点')
# 生成平滑的曲线
x_smooth = np.linspace(min(feat), max(feat), 300)
if self.model_type:
y_smooth = self.predict_concentration(x_smooth)
plt.plot(x_smooth, y_smooth, color='red', label='拟合曲线')
plt.xlabel('RGB特征 (e.g., R_normalized)')
plt.ylabel('浓度')
plt.title('浓度 vs. RGB特征 校准曲线')
plt.legend()
plt.grid(True)
if save_path:
plt.savefig(save_path)
plt.show()
def save_model(self, filepath):
"""保存模型参数到文件"""
model_data = {
'model_type': self.model_type,
'model_params': self.model_params,
'feature_name': self.feature_name
}
joblib.dump(model_data, filepath)
print(f"模型已保存至 {filepath}")
def load_model(self, filepath):
"""从文件加载模型参数"""
model_data = joblib.load(filepath)
self.model_type = model_data['model_type']
self.model_params = model_data['model_params']
self.feature_name = model_data['feature_name']
print(f"模型已从 {filepath} 加载。")
# 示例:如何使用
if __name__ == '__main__':
cal_model = ConcentrationModel()
# 方法1: 拟合一个二次多项式
r2_poly = cal_model.fit_polynomial(degree=2)
conc_pred = cal_model.predict_concentration(0.6)
print(f"对于特征值0.6,预测浓度为: {conc_pred:.2f}")
cal_model.plot_calibration_curve()
cal_model.save_model('./models/poly_calibration_model.joblib')
# 方法2: 拟合一个S型曲线
# cal_model.model_type = 'sigmoid'
# r2_sigmoid = cal_model.fit_curve(cal_model.sigmoid_model)
# 方法3: 加载一个已有的模型
# new_model = ConcentrationModel()
# new_model.load_model('./models/poly_calibration_model.joblib')
# result = new_model.predict_concentration(0.55)
关键点说明:
curve_fit
:非常强大的函数,可以拟合任何您定义的数学模型。- 模型评估:始终使用决定系数(R²) 和均方误差(MSE) 等指标来评估拟合优度。R²越接近1越好。
- 随机森林:能处理多个特征,并且不需要预先假设模型形式,但可解释性不如参数模型。
- 模型持久化:使用
joblib
保存训练好的模型参数,这样在部署时就不需要重新训练了。
第六部分:系统集成与完整工作流
现在我们将所有模块组合起来,形成一个完整的流程。
class FluorescenceAnalysisSystem:
def __init__(self, yolo_model_path, calibration_model_path):
self.detector = FluorescenceDetector(yolo_model_path)
self.cal_model = ConcentrationModel()
self.cal_model.load_model(calibration_model_path)
def analyze_image(self, image_path):
"""
完整的分析流程
1. 检测并裁剪ROI
2. 提取RGB特征
3. 预测浓度
"""
print(f"开始分析图像: {image_path}")
# Step 1: YOLO检测与裁剪
rois = self.detector.predict_and_crop(image_path)
if not rois:
return {"error": "未在图像中检测到目标区域"}
results = []
for i, roi_info in enumerate(rois):
roi_img = roi_info['roi_image']
# Step 2: 提取RGB特征
# 可以选择预处理 roi_img = self.detector.preprocess_image(roi_img)
rgb_values = self.detector.extract_dominant_rgb(roi_img, method='max_area')
rgb_ratios = self.detector.calculate_rgb_ratios(rgb_values)
# Step 3: 预测浓度
# 假设我们的校准模型使用的是 'r_norm' 特征
feature_for_prediction = rgb_ratios['r_norm']
try:
predicted_concentration = self.cal_model.predict_concentration(feature_for_prediction)
except Exception as e:
predicted_concentration = None
print(f"浓度预测失败: {e}")
# 收集结果
result = {
'roi_id': i,
'bbox': roi_info['bbox'],
'confidence': roi_info['confidence'],
'rgb_values': rgb_values,
'rgb_ratios': rgb_ratios,
'predicted_concentration': predicted_concentration
}
results.append(result)
print(f"ROI {i} - RGB: {rgb_values}, R_normalized: {feature_for_prediction:.3f}, 预测浓度: {predicted_concentration}")
return results
# 示例:完整流程
if __name__ == '__main__':
# 初始化整个系统
system = FluorescenceAnalysisSystem(
yolo_model_path='./models/fluorescence_detector.pt',
calibration_model_path='./models/poly_calibration_model.joblib'
)
# 分析用户上传的图像
analysis_results = system.analyze_image('new_unknown_sample.jpg')
# 打印结果
for res in analysis_results:
print(f"检测区域 {res['roi_id']} 的浓度为: {res['predicted_concentration']:.4f} units")
第七部分:扩展思路与优化方向
-
Web应用集成(Flask示例):
from flask import Flask, request, jsonify, render_template import os from werkzeug.utils import secure_filename app = Flask(__name__) app.config['UPLOAD_FOLDER'] = './uploads' app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB limit # 在应用启动时加载系统(重量级对象,只加载一次) analysis_system = None def get_analysis_system(): global analysis_system if analysis_system is None: analysis_system = FluorescenceAnalysisSystem( './models/fluorescence_detector.pt', './models/poly_calibration_model.joblib' ) return analysis_system @app.route('/') def index(): return render_template('upload.html') @app.route('/api/analyze', methods=['POST']) def analyze_api(): if 'file' not in request.files: return jsonify({'error': 'No file uploaded'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No selected file'}), 400 if file: filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(filepath) system = get_analysis_system() results = system.analyze_image(filepath) # 清理上传的文件(可选) os.remove(filepath) return jsonify({'results': results}) if __name__ == '__main__': os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) app.run(debug=True)
-
数据库集成:使用SQLite或PostgreSQL存储每次分析的结果、原始图像和提取的特征,用于后续模型重训练和优化。
-
高级特性:
- 多目标支持:同时处理图像中的多个孔或试管。
- 背景扣除:拍摄一张空白对照图片,从样本图片中减去背景颜色。
- 质量控制(QC):检查提取的RGB值是否在合理的范围内,或者检测的置信度是否足够高,否则向用户发出警告。
- 不确定性估计:基于校准数据的拟合残差,为预测浓度提供一个置信区间。
总结
本项目详细阐述了一个基于YOLO和RGB分析的荧光浓度检测系统的完整实现方案。从YOLO模型的准备与推理、ROI的精确提取和预处理,到RGB特征的计算与校准模型的建立,最后到系统的集成与部署,涵盖了技术细节和代码实现。
核心要点:
- YOLO用于定位:准确找到感兴趣区域是第一步,也是排除干扰的关键。
- 鲁棒的颜色提取:使用
max_area
等策略和图像预处理来稳定RGB值。 - 基于比值的特征:归一化比值对光照变化更不敏感。
- 校准模型是关键:必须通过实验建立可靠、高精度的校准曲线,并持续验证和更新。
- 系统化集成:将CV、ML和软件工程结合,构建一个健壮、可用的应用程序。
这个系统具有很强的实用性和可扩展性,可以根据具体的荧光检测应用场景进行调整和优化。
更多推荐
所有评论(0)