如何用Python准确计数航拍图像中的树木及获取轮廓数量
解决航拍图像树木计数问题的分步方案
嘿,作为图像处理新手,你已经迈出了很棒的第一步!咱们一步步拆解你的问题,先搞定计数错误的根源,再解决轮廓数量的获取问题。
一、最初计数结果为7的原因分析
你最初的流程里,虽然做了HSV分割和高斯模糊,但分割后的绿色区域大概率是大片连在一起的——航拍图里树木往往挨得近,高斯模糊又会让边缘更模糊,导致多个树木被当成了一个连通区域,所以measure.label(drops)只识别出7个大区域,而不是单个树木。
二、获取轮廓数量的正确方法
你补充的代码里,contours是OpenCV找到的轮廓列表,想要获取数量直接用len(contours)就可以啦!不过要注意:
- 不要用
contour.size()(这是单个轮廓的方法,你遍历的是每个cnt,而contours是列表) - 建议过滤掉面积太小的轮廓,避免把噪声误当成树木,比如:
tree_count = 0 min_contour_area = 100 # 根据你的图像大小调整这个值 for cnt in contours: if cv2.contourArea(cnt) > min_contour_area: tree_count += 1 cv2.drawContours(finalImage,[cnt],0,(0,0,255),1) print(f"树木数量:{tree_count}")
三、优化后的完整树木计数流程
针对航拍图树木计数,我们需要调整预处理和分割步骤,让相邻树木尽可能分开:
1. 更精准的HSV绿色分割
航拍图的绿色可能因为光照、阴影有变化,你可以调整HSV阈值,或者用更灵活的范围;另外,不要先转PIL的HSV再存盘读取,直接用OpenCV处理更高效:
import cv2 import numpy as np from skimage import measure # 直接读取原始图像 img = cv2.imread("D:\\Texture analysis\\K-2.jpg") hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 调整HSV阈值,适配航拍绿色(可以根据实际图像微调) lower_green = np.array([30, 20, 20]) upper_green = np.array([80, 255, 255]) mask = cv2.inRange(hsv, lower_green, upper_green) # 先去除小噪声(开运算:先腐蚀后膨胀,比单纯腐蚀更能保留树木形状) kernel = np.ones((5,5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2) # 填充树木内部的孔洞 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=3)
2. 分离相邻树木的关键操作
如果树木还是连在一起,可以用距离变换+阈值分割来分开它们:
# 距离变换:计算每个前景像素到背景的距离 dist_transform = cv2.distanceTransform(mask, cv2.DIST_L2, 5) # 阈值化,得到单个树木的种子区域 ret, sure_fg = cv2.threshold(dist_transform, 0.3*dist_transform.max(), 255, 0) sure_fg = np.uint8(sure_fg) # 找到未知区域(用于分水岭算法) sure_bg = cv2.dilate(mask, kernel, iterations=3) unknown = cv2.subtract(sure_bg, sure_fg) # 标记种子区域 ret, markers = cv2.connectedComponents(sure_fg) markers = markers + 1 markers[unknown==255] = 0 # 应用分水岭算法分割相邻树木 markers = cv2.watershed(img, markers) img[markers == -1] = [255,0,0] # 用红色标记分割线
3. 最终计数
用measure.label或者轮廓计数都可以:
# 方法1:用skimage的label计数 labels = measure.label(sure_fg) print(f"树木数量(skimage):{labels.max()}") # 方法2:用OpenCV轮廓计数 contours, hierarchy = cv2.findContours(sure_fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤小轮廓 tree_count = sum(1 for cnt in contours if cv2.contourArea(cnt) > 150) print(f"树木数量(OpenCV):{tree_count}")
四、小提示
- 如果你不确定HSV阈值,可以用OpenCV的滑动条工具实时调整,找到最适合你图像的范围
- 形态学操作的kernel大小和迭代次数需要根据图像分辨率调整,比如高分辨率图可以用更大的kernel
- 分水岭算法对种子区域的质量很敏感,调整
0.3*dist_transform.max()这个系数可以优化分割效果
内容的提问来源于stack exchange,提问作者Kamil Hassaan
相关产品推荐
相关产品推荐

