You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R或Python中半自动检测并量化叶片受损面积?

解决方案:R/Python 半自动叶片受损百分比计算

R 实现方案

基于你已使用的pliman包,结合imager和geometry包完成需求:

1. 单叶片识别

针对白色背景,通过灰度阈值分割分离叶片与背景,再检测连通区域实现单叶片识别:

library(pliman)
library(imager)
library(geometry)

# 读取图像
img <- image_read("leaf_image.jpg")

# 灰度化+二值化分割(根据图像调整阈值方向)
seg <- segment(img, threshold = "otsu", invert = TRUE)  # invert=TRUE适用于叶片比背景暗的场景
# 检测独立叶片对象(过滤小噪声)
leaf_objects <- object_detect(seg, min_area = 100)

2. 凸包补全缺失边缘

用geometry::convhulln生成每个叶片的凸包,补全受损缺失的边缘:

# 为每个叶片生成凸包顶点
hulls <- lapply(leaf_objects$coords, function(coord) {
  ch <- convhulln(coord[, c("x", "y")])
  coord[unique(as.vector(ch)), ]
})

# 可视化验证凸包(可选)
plot(img)
lapply(hulls, function(h) lines(h$x, h$y, col = "red", lwd = 2))

3. 面积计算(受损区+补全后总面积)

通过HSV颜色空间提取红色受损区域,结合凸包面积计算百分比:

# 交互选择红色区域(更精准,推荐)
red_mask <- select_color(img, color = "red", plot = TRUE)

# 或手动设置HSV阈值(根据实际图像调整)
# red_mask <- color_space(img, space = "hsv") %>%
#   filter(H < 10 | H > 350, S > 0.3, V > 0.2) %>%
#   image_mask()

# 计算每个叶片的面积指标(假设1cm对应100像素,根据你的尺度调整)
pixel_per_cm <- 100
results <- lapply(seq_along(leaf_objects$coords), function(i) {
  # 原始叶片面积
  leaf_area <- object_area(leaf_objects$coords[[i]], pixel_per_cm = pixel_per_cm)
  # 凸包补全后的总面积
  hull_area <- convhulln(leaf_objects$coords[[i]][, c("x", "y")], area = TRUE)$area / (pixel_per_cm^2)
  # 叶片内的受损区域面积
  red_in_leaf <- image_crop(red_mask, leaf_objects$bbox[[i]])
  damaged_area <- sum(red_in_leaf) / (pixel_per_cm^2)
  
  data.frame(
    leaf_id = i,
    original_leaf_area_cm2 = round(leaf_area, 2),
    convex_hull_total_cm2 = round(hull_area, 2),
    damaged_area_cm2 = round(damaged_area, 2),
    damage_percentage = round((damaged_area / hull_area) * 100, 2)
  )
})

# 合并结果
results_df <- do.call(rbind, results)
print(results_df)

Python 实现方案

用OpenCV+SciPy实现,适合偏好Python的场景:

1. 单叶片识别

通过灰度阈值+轮廓检测分离独立叶片:

import cv2
import numpy as np
from scipy.spatial import ConvexHull

# 读取图像
img = cv2.imread("leaf_image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化分割(OTSU自动阈值,反转适用于叶片暗、背景白)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 去除噪声
kernel = np.ones((3,3), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 提取叶片轮廓(过滤小噪声轮廓)
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
leaf_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 100]

2. 凸包补全缺失边缘

用OpenCV内置的convexHull生成凸包:

# 生成每个叶片的凸包
hulls = [cv2.convexHull(cnt) for cnt in leaf_contours]

# 可视化验证(可选)
vis = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.drawContours(vis, leaf_contours, -1, (0,255,0), 2)
cv2.drawContours(vis, hulls, -1, (255,0,0), 2)
cv2.imshow("Leaves + Convex Hulls", vis)
cv2.waitKey(0)

3. 面积计算

提取红色受损区域,计算各面积指标:

# HSV空间提取红色(覆盖红色的两个HSV区间)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red1 = np.array([0, 50, 50])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 50, 50])
upper_red2 = np.array([180, 255, 255])
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
red_mask = cv2.bitwise_or(mask1, mask2)
red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_OPEN, kernel, iterations=2)

# 尺度参数:1cm对应像素数(根据你的参考尺度调整)
pixel_per_cm = 100
pixel_per_cm2 = pixel_per_cm ** 2

# 计算每个叶片的结果
results = []
for i, (cnt, hull) in enumerate(zip(leaf_contours, hulls), 1):
    # 原始叶片面积
    leaf_area = cv2.contourArea(cnt) / pixel_per_cm2
    # 凸包总面积
    hull_area = cv2.contourArea(hull) / pixel_per_cm2
    # 生成叶片掩码,提取叶片内的受损区域
    leaf_mask = np.zeros_like(gray)
    cv2.drawContours(leaf_mask, [cnt], 0, 255, -1)
    damaged_mask = cv2.bitwise_and(red_mask, leaf_mask)
    damaged_area = cv2.countNonZero(damaged_mask) / pixel_per_cm2
    # 受损百分比
    damage_pct = round((damaged_area / hull_area) * 100, 2) if hull_area > 0 else 0
    
    results.append({
        "leaf_id": i,
        "original_leaf_area_cm2": round(leaf_area, 2),
        "convex_hull_total_cm2": round(hull_area, 2),
        "damaged_area_cm2": round(damaged_area, 2),
        "damage_percentage": damage_pct
    })

# 输出结果
for res in results:
    print(res)

关键提示

  • 阈值参数(灰度、HSV)需根据你的实际图像微调,交互选色工具(R的select_color)能大幅提升准确率
  • 若叶片与背景灰度差异小,可尝试Canny边缘检测结合轮廓提取
  • 半自动场景下,可手动框选ROI(R的select_roi、Python的cv2.selectROI)缩小分析范围,减少噪声干扰

内容的提问来源于stack exchange,提问作者M Sandoval

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:57:56