1067 字
5 分钟
CV: 识别10种不同的叶子

CV:识别10种不同的叶子#

要求:#

识别树叶形状。

本次实验提供了十种不同植物的叶子图像,每一种植物大约10张叶子图像,图像已经处理为黑白图像。请设计合理的形状描述方法,以及分类算法,实现对这些树叶的分类。

形状特征请参考阅读冈萨雷斯《Digital Image Processing》第四版第11章。 分类器可以使用Softmax Classifier, Naive Bayes,K-近邻,神经网络等。

请从每一种叶子中随机选取60%用于训练你的算法,剩下的40%用于测试你的算法。

下面是两种不同的叶子的图片:

Issue.png (1487×960) (fragments.work)

完整的图片请见:下载页面 (fragments.work)

思路#

1. 提取Circularity特征#

由于cv2提供了cv2.contourArea(countour)perimeter = cv2.arcLength(countour, closed=True)计算闭合轮廓的面积AA和周长pp,根据公式:

circularity=4πAp2circularity = \frac{4\pi A}{p^2}

很容易得到circularity特征值。

def getCircularity(countour):
    area = cv2.contourArea(countour)
    perimeter = cv2.arcLength(countour, closed=True)
    return 4 * np.pi * area / perimeter ** 2

2. 提取eccentric特征值#

离心率的计算可以按照冈萨雷斯《Digital Image Processing》书中的计算方法,计算特征点的协方差阵,取最大和最小的特征值作为拟合的椭圆的主轴距离和次轴距离。

def getEccentricity(countour):
    countourPoint = countour.reshape(-1, 2).astype(np.float32)
    M = np.mean(countourPoint, axis=0)
    # 计算中心
    points_centered = countourPoint - M
    # 协方差阵
    cov_matrix = np.cov(points_centered, rowvar=False)
    # 特征值分解,找最大和最小的特征值
    lamda = np.linalg.eig(cov_matrix)[0]
    l1 = np.max(lamda)
    l2 = np.min(lamda)
    ec2 = np.sqrt(1 - l2**2 / l1**2)
    return ec2

不过也可以直接利用cv2.ellipse = cv2.fitEllipse(countour)来拟合轮廓的椭圆,这样也可以得到椭圆的长半轴长和短轴长。

ellipse = cv2.fitEllipse(countour)  # 轮廓的拟合椭圆
 (x, y), (a, b), ang = np.int32(ellipse[0]), np.int32(ellipse[1]),
    round(ellipse[2], 1) # a,b即是两轴长度

3. 提取HuMoments特征值#

直接利用现成的cv2.momentscv2.HuMoments即可。 不过可以像书上说的,取10的对数后再取绝对值消除镜像时的’-‘号

def getMoments(countour):
    M = cv2.moments(countour)
    huMoments = cv2.HuMoments(M)
    return np.abs(np.log10(np.abs(huMoments))).reshape(-1)

4. 提取凸包特征值#

由于一开始并没有添加这个特征值,导致分类很不准确。

特别是这个4号树叶,和1号树叶长得形态几乎是一样的,但是发现4号树叶有明显的边缘角点,查阅资料以后,可以使用cv2.convexHullcv2.convexityDefects检测出角点,大概是这样的:

于是想到可以利用这个锯齿状角点的数量和相连接这些凸包连接起来的那两条边的长度的和来作为特征。

不过要加一些限制,因为树叶上有很多很多非常小的锯齿状,不能把很小很小的锯齿状也认为是锯齿[实测效果很差],只看大的锯齿。具体的做法是,锯齿的两条边的长度和大于一定阈值,并且开口的角度处于[45,135][45,135]​之间。

5. 拟合模型#

通过上述方法能提取一个dim=11dim = 11的特征向量,把这个特征向量放到SVMSVM里,SVMSVM的参数全部默认[kernel='rbf',C=1.0],取每一类的60%60\%训练,剩下的测试。

混淆矩阵是这样的:

matrix.png (1139×425) (fragments.work)

SVMSVM的测试集正确率能来到91.48%91.48\%,不过这是我选取的最好的情况,一般在80%-90%之间浮动。

后记#

感觉是数据集太小了,所以不同的训练集/测试集划分方法可能会引起大概测试正确率10%10\%的变动,由于SVMSVM使用了非线性核,所以没有coef_,就输出SGDClassfier的权重吧。每一列的是一个类别的叶子,每一行代表一个特征,比如一号,二号叶子对离心率都很敏感,因为它们的叶片都呈现椭圆形,所以这个椭圆一些比较好。同理,对近圆形也很敏感对于角点数量hullhull,可以看到没有大锯齿的1,2,3号叶子都是负的,但是对于有很多大锯齿的4,9,10号都是正的。

hotGraph.png (640×480) (fragments.work)

还是有一些分不出来,猜测是因为有一些叶子太像了吧。。。

error2.jpg (1459×960) (fragments.work)

CV: 识别10种不同的叶子
http://blog.fragments.work/posts/blogs/cv-lab5/
作者
Lixin WANG
发布于
2024-06-18
许可协议
CC BY-NC-SA 4.0