import os
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
import re
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
class FastScratchDetector:
"""
快速的浅层机械损伤检测器
优化了计算效率,保持合理的检测精度
"""
def __init__(self):
# 优化的检测参数
self.min_scratch_length = 50
self.max_scratch_width = 4
self.min_aspect_ratio = 8
self.hough_threshold = 40
self.min_line_length = 40
def preprocess_image_fast(self, image_path):
"""快速图像预处理"""
try:
# 读取图像并立即缩小尺寸
img = cv2.imread(image_path)
if img is None:
return None
# 保存原始图像用于可视化
original = img.copy()
# 快速调整图像大小 - 直接缩小到固定尺寸
h, w = img.shape[:2]
if max(h, w) > 400: # 使用更小的尺寸
scale = 400 / max(h, w)
new_size = (int(w * scale), int(h * scale))
img = cv2.resize(img, new_size)
original = cv2.resize(original, new_size)
# 简单的灰度转换和直方图均衡化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
enhanced = clahe.apply(gray)
return {
'original': original,
'gray': enhanced,
'resized': img
}
except Exception as e:
print(f"预处理错误: {e}")
return None
def detect_scratch_fast(self, gray_img, original_img):
"""快速划痕检测"""
# 1. 边缘检测
edges = cv2.Canny(gray_img, 50, 150)
# 2. 线段检测 - 只使用单一尺度
lines = cv2.HoughLinesP(
edges,
rho=1,
theta=np.pi / 180,
threshold=self.hough_threshold,
minLineLength=self.min_line_length,
maxLineGap=10
)
line_count = len(lines) if lines is not None else 0
# 3. 快速轮廓检测
binary = cv2.adaptiveThreshold(
gray_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2
)
# 简单的形态学操作
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
contours, _ = cv2.findContours(
binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
valid_contours = []
for contour in contours:
area = cv2.contourArea(contour)
if area < 20 or area > 800:
continue
rect = cv2.minAreaRect(contour)
width, height = rect[1]
length = max(width, height)
width = min(width, height)
if width > 0:
aspect_ratio = length / width
else:
aspect_ratio = 0
if (length >= self.min_scratch_length and
width <= self.max_scratch_width and
aspect_ratio >= self.min_aspect_ratio):
valid_contours.append(contour)
# 快速病害检测 - 基于简单的颜色阈值
has_disease = self.fast_disease_check(gray_img)
# 判断逻辑:有划痕特征且没有病害
has_scratch = line_count >= 2 or len(valid_contours) >= 1
has_mechanical_damage = has_scratch and not has_disease
return {
'has_mechanical_damage': has_mechanical_damage,
'line_count': line_count,
'contour_count': len(valid_contours),
'has_disease': has_disease,
'lines': lines,
'contours': valid_contours,
'edges': edges,
'binary': binary
}
def fast_disease_check(self, gray_img):
"""快速病害检测"""
# 使用简单的亮度阈值检测暗色区域(可能的病害)
_, dark_regions = cv2.threshold(gray_img, 50, 255, cv2.THRESH_BINARY_INV)
# 计算暗色区域比例
dark_ratio = np.sum(dark_regions > 0) / (gray_img.shape[0] * gray_img.shape[1])
# 如果暗色区域超过5%,认为可能有病害
return dark_ratio > 0.05
def analyze_image_fast(self, image_path):
"""快速分析图像"""
preprocessed = self.preprocess_image_fast(image_path)
if preprocessed is None:
return None
result = self.detect_scratch_fast(preprocessed['gray'], preprocessed['original'])
result['preprocessed'] = preprocessed
return result
def visualize_detection_fast(self, analysis_result, save_path=None):
"""快速可视化检测结果"""
if analysis_result is None:
return
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
fig.suptitle('石榴浅层机械损伤快速检测分析', fontsize=16, fontweight='bold')
original = analysis_result['preprocessed']['original']
gray = analysis_result['preprocessed']['gray']
# 原始图像
axes[0, 0].imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))
axes[0, 0].set_title('原始图像')
axes[0, 0].axis('off')
# 预处理后图像
axes[0, 1].imshow(gray, cmap='gray')
axes[0, 1].set_title('预处理图像')
axes[0, 1].axis('off')
# 边缘检测结果
axes[0, 2].imshow(analysis_result['edges'], cmap='gray')
axes[0, 2].set_title('边缘检测')
axes[0, 2].axis('off')
# 二值化图像
axes[1, 0].imshow(analysis_result['binary'], cmap='gray')
axes[1, 0].set_title('二值化图像')
axes[1, 0].axis('off')
# 检测结果可视化
result_img = original.copy()
# 绘制线段
if analysis_result['lines'] is not None:
for line in analysis_result['lines']:
x1, y1, x2, y2 = line[0]
cv2.line(result_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 绘制轮廓
for contour in analysis_result['contours']:
cv2.drawContours(result_img, [contour], -1, (255, 0, 0), 2)
axes[1, 1].imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB))
axes[1, 1].set_title('检测结果\n(绿:线段, 蓝:轮廓)')
axes[1, 1].axis('off')
# 检测统计
axes[1, 2].axis('off')
stats_text = f"""
检测统计:
==========
线段数量: {analysis_result['line_count']}
有效轮廓: {analysis_result['contour_count']}
病害迹象: {'是' if analysis_result['has_disease'] else '否'}
机械损伤: {'是' if analysis_result['has_mechanical_damage'] else '否'}
"""
axes[1, 2].text(0.1, 0.5, stats_text, fontsize=12, va='center', linespacing=1.8)
axes[1, 2].set_title('检测统计')
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"可视化结果已保存: {save_path}")
plt.show()
class DetectionAnalyzer:
"""检测结果分析器"""
def __init__(self):
self.detection_results = []
self.weekly_stats = {}
def add_result(self, week, tree_id, fruit_id, has_damage, line_count, contour_count, has_disease):
"""添加检测结果"""
self.detection_results.append({
'week': week,
'tree_id': tree_id,
'fruit_id': fruit_id,
'has_damage': has_damage,
'line_count': line_count,
'contour_count': contour_count,
'has_disease': has_disease
})
# 更新周统计
if week not in self.weekly_stats:
self.weekly_stats[week] = {'total': 0, 'damaged': 0, 'diseased': 0}
self.weekly_stats[week]['total'] += 1
if has_damage:
self.weekly_stats[week]['damaged'] += 1
if has_disease:
self.weekly_stats[week]['diseased'] += 1
def generate_comprehensive_report(self, total_processed, total_detected):
"""生成综合分析报告"""
if not self.detection_results:
print("没有检测数据可生成报告")
return
df = pd.DataFrame(self.detection_results)
# 创建综合报告
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
fig.suptitle('石榴浅层机械损伤检测综合分析报告', fontsize=18, fontweight='bold')
# 1. 每周损伤分布
weekly_damage = df.groupby('week')['has_damage'].sum()
weeks = list(weekly_damage.index)
damage_counts = list(weekly_damage.values)
axes[0, 0].bar(weeks, damage_counts, color='lightcoral', alpha=0.7, edgecolor='darkred')
axes[0, 0].set_xlabel('周数')
axes[0, 0].set_ylabel('损伤果实数量')
axes[0, 0].set_title('各周机械损伤分布')
axes[0, 0].grid(True, alpha=0.3)
# 在柱状图上添加数值标签
for i, v in enumerate(damage_counts):
axes[0, 0].text(weeks[i], v + 0.1, str(v), ha='center', va='bottom')
# 2. 总体检测比例
total_healthy = total_processed - total_detected
pie_data = [total_detected, total_healthy]
pie_labels = [f'机械损伤\n({total_detected})', f'健康果实\n({total_healthy})']
pie_colors = ['lightcoral', 'lightgreen']
axes[0, 1].pie(pie_data, labels=pie_labels, colors=pie_colors,
autopct='%1.1f%%', startangle=90, textprops={'fontsize': 10})
axes[0, 1].set_title('总体检测比例')
# 3. 每周检测率
weekly_rates = []
for week in sorted(self.weekly_stats.keys()):
stats = self.weekly_stats[week]
rate = stats['damaged'] / stats['total'] * 100 if stats['total'] > 0 else 0
weekly_rates.append(rate)
axes[0, 2].plot(sorted(self.weekly_stats.keys()), weekly_rates,
marker='o', linewidth=2, markersize=8, color='steelblue')
axes[0, 2].set_xlabel('周数')
axes[0, 2].set_ylabel('检测率 (%)')
axes[0, 2].set_title('每周机械损伤检测率')
axes[0, 2].grid(True, alpha=0.3)
axes[0, 2].set_ylim(0, max(weekly_rates) * 1.2 if weekly_rates else 10)
# 4. 特征分布 - 线段数量
line_counts = df['line_count']
axes[1, 0].hist(line_counts, bins=20, color='skyblue', alpha=0.7, edgecolor='black')
axes[1, 0].set_xlabel('线段数量')
axes[1, 0].set_ylabel('频次')
axes[1, 0].set_title('线段数量分布')
axes[1, 0].grid(True, alpha=0.3)
# 5. 特征分布 - 轮廓数量
contour_counts = df['contour_count']
axes[1, 1].hist(contour_counts, bins=20, color='lightgreen', alpha=0.7, edgecolor='black')
axes[1, 1].set_xlabel('轮廓数量')
axes[1, 1].set_ylabel('频次')
axes[1, 1].set_title('有效轮廓数量分布')
axes[1, 1].grid(True, alpha=0.3)
# 6. 统计摘要
axes[1, 2].axis('off')
stats_text = f"""
检测统计摘要
============
总处理图像: {total_processed}
损伤果实数: {total_detected}
健康果实数: {total_healthy}
总体检测率: {total_detected / total_processed * 100:.2f}%
特征统计:
- 平均线段数: {df['line_count'].mean():.2f}
- 平均轮廓数: {df['contour_count'].mean():.2f}
- 病害果实数: {df['has_disease'].sum()}
周统计:
- 最高损伤周: 第{weekly_damage.idxmax()}周 ({weekly_damage.max()}个)
- 最低损伤周: 第{weekly_damage.idxmin()}周 ({weekly_damage.min()}个)
- 平均每周: {weekly_damage.mean():.1f}个
"""
axes[1, 2].text(0.05, 0.95, stats_text, fontsize=11, va='top', linespacing=1.6,
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgray", alpha=0.7))
plt.tight_layout()
plt.savefig('detection_comprehensive_report.png', dpi=300, bbox_inches='tight')
plt.show()
return df
def parse_filename(filename):
"""解析文件名"""
try:
if '_' in filename and '.' in filename:
parts = filename.split('_')
if len(parts) >= 2:
tree_id = int(parts[0])
fruit_sub_id = int(parts[1].split('.')[0])
return tree_id, fruit_sub_id
elif '.' in filename:
parts = filename.split('.')
if len(parts) >= 3:
tree_id = int(parts[0])
fruit_sub_id = int(parts[1])
return tree_id, fruit_sub_id
except:
pass
return 1, 1
def main_fast():
"""快速版本的主程序"""
print("=" * 60)
print("石榴浅层机械损伤检测系统 (快速版本)")
print("=" * 60)
# 初始化快速检测器和分析器
detector = FastScratchDetector()
analyzer = DetectionAnalyzer()
# 数据路径
data_dir = "D:/university/shujuwajue/shiyong1/data/attachment1"
if not os.path.exists(data_dir):
print(f"错误: 数据路径不存在 - {data_dir}")
return
# 存储最终结果
final_results = []
# 遍历数据
total_processed = 0
total_detected = 0
visualization_count = 0
max_visualizations = 5 # 最多生成5个可视化结果
week_dirs = [d for d in Path(data_dir).iterdir() if d.is_dir() and 'week' in d.name.lower()]
for week_dir in sorted(week_dirs):
week_match = re.search(r'week_(\d+)_', week_dir.name)
if week_match:
week = int(week_match.group(1))
else:
week = 1
print(f"\n处理第 {week} 周数据...")
# 遍历树号文件夹
tree_dirs = [d for d in week_dir.iterdir() if d.is_dir() and d.name.isdigit()]
for tree_dir in tree_dirs:
tree_id = int(tree_dir.name)
print(f" 树号: {tree_id:02d}", end=" - ")
# 处理该树的所有图像
image_files = list(tree_dir.glob("*.jpg"))
tree_detected = 0
for img_path in image_files:
total_processed += 1
# 解析果实编号
parsed_tree_id, fruit_sub_id = parse_filename(img_path.name)
fruit_id = int(f"{parsed_tree_id:02d}{fruit_sub_id:02d}")
# 分析图像
analysis = detector.analyze_image_fast(str(img_path))
if analysis is not None:
has_damage = analysis['has_mechanical_damage']
# 记录到分析器
analyzer.add_result(
week, tree_id, fruit_id, has_damage,
analysis['line_count'], analysis['contour_count'], analysis['has_disease']
)
# 记录结果
if has_damage:
final_results.append({
'周数': week,
'果实编号': fruit_id
})
total_detected += 1
tree_detected += 1
# 为前几个检测到的损伤生成可视化
if visualization_count < max_visualizations:
viz_path = f"detection_visualization_{week}_{tree_id}_{fruit_sub_id}.png"
detector.visualize_detection_fast(analysis, viz_path)
visualization_count += 1
print(f"检测到 {tree_detected} 个损伤")
# 保存最终结果
if final_results:
df_results = pd.DataFrame(final_results)
df_results = df_results.sort_values(['周数', '果实编号']).drop_duplicates()
df_results.to_excel('result4_fast.xlsx', index=False)
print(f"\n" + "=" * 60)
print("检测完成!")
print(f"处理图像总数: {total_processed}")
print(f"检测到机械损伤: {total_detected}")
print(f"检测率: {total_detected / total_processed * 100:.2f}%")
print(f"结果已保存至: result4_fast.xlsx")
# 显示检测到的果实信息
print(f"\n检测到的损伤果实分布:")
weekly_counts = df_results['周数'].value_counts().sort_index()
for week, count in weekly_counts.items():
print(f" 第{week}周: {count}个")
# 生成综合分析报告
print(f"\n生成综合分析报告...")
analyzer.generate_comprehensive_report(total_processed, total_detected)
else:
print("\n未检测到任何机械损伤")
# 创建空的结果文件
empty_df = pd.DataFrame(columns=['周数', '果实编号'])
empty_df.to_excel('result4_fast.xlsx', index=False)
if __name__ == "__main__":
# 运行快速版本
main_fast()这段代码如何修改可以拿到数据挖掘比赛的高分,给我完整的代码
最新发布