opencv之基本形状识别

opencv之基本形状识别

各种博客上的现有方法

  1. https://blog.csdn.net/xuxunjie147/article/details/76577298 这篇博客只是展示了代码,并没有讲解解决问题的思路。笔者通过阅读其代码,将其思路总结如下:
    在这里插入图片描述
  2. https://www.cnblogs.com/long5683/p/9694983.html 这篇博客使用面积和多边形拟合后的顶点数进行各种形状的分类。但是这种方法只是适应于他给的那张图片。
  3. https://blog.51cto.com/gloomyfish/2104134?lb 这篇博客的方法主要是通过arcLength计算原始轮廓的周长,然后通过多边形拟合逼近原始周长。最后使用多边形拟合得到的顶点数进行分类。三个顶点就是三角形,4个顶点就是矩形,4-10个顶点是不规则多边形,10个以上是圆。但是对于不规则的四边形,这种方法显然会失效。因为该方法并没有考虑每个顶点处的角度。
  4. https://www.jianshu.com/p/2731f42882f4 这篇博客所使用的方法思路与上一篇博客基本类似。

新方法——从信号的角度分析

  1. 具体步骤如下:
  • 使用findcontours找到所要识别的图形的轮廓。
  • 在轮廓的基础上,使用图形的矩(moments)计算得到图形的重心。
  • 使用drawcontours绘制轮廓,并得边缘的坐标位置
  • 计算边缘上所有的点到重心的距离,按照逆时针绕重心转动的方向绘制距离随角度变化的图。
  • 进行距离归一化

实验结果

  1. 圆形及其对应的距离-角度变化图
  1. 椭圆及其对应的距离-角度变化图
  1. 五边形及其对应的距离-角度变化图
  1. 矩形及其对应的距离-角度变化图
  1. 正方形及其对应的距离-角度变化图
  1. 平行四边形及其对应的距离-角度变化图
  1. 三角形及其对应的距离-角度变化图

通过上面这些图可以得到图下结论:

  • 多边形(不包括圆和椭圆)的距离-角度图像存在大量导数不存在的点,且不可导点的数量等于顶点的个数。
  • 多边形的顶点对应的角度越大,其不可导点的尖锐程度越小,即左导数和右导数的差距越小。
  • 对称图形的距离-角度图像呈现高度的对称性甚至周期性

附代码

程序是早些时候写得,没有封装成独立的函数,还有些啰嗦。代码是进行批处理的,读者可以稍微改一下程序中使用的路径。

大体的思路是先找到轮廓,然后根据轮廓计算重心位置和轮廓各个点到重心的距离。但是opencv的findcontours只会返回决定轮廓的几个重要的点,比如矩形轮廓只会返回4个顶点的坐标。为了得到所有轮廓的点,使用drawcontours先把轮廓画出来,然后根据像素值找到轮廓所有点的坐标位置。由于轮廓是画出来的,所以drawcontours的linewidth参数相当于控制绘制轮廓精细程度的一个参数。

代码中还使用fft和差分这两种时间序列分析手段分析我们的得到的数据。希望能对读者有所帮助。

代码很久没有运行,如果有bug,还望海涵。

# -*- coding:utf8 -*-
# @TIME     : 2019/7/11 16:03
# @Author   : SuHao
# @File     : main.py

import numpy as np
import cv2
import os
import matplotlib.pyplot as plt
import scipy.fftpack as fft


path_pic = "./pic"
filename = os.listdir(path_pic)
path_result = "./result"
if not os.path.exists(path_result):
    os.mkdir(path_result)

for i in range(len(filename)):
    image = cv2.imread(path_pic + "/" + filename[i], 0)
    ret, thresh = cv2.threshold(image, 100, 255, 0)

    #计算轮廓
    out, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    drawing = np.zeros(image.shape) + 255
    imgnew = cv2.drawContours(drawing, contours, 1, (0, 255, 255), 0)

    #确定轮廓的重心
    mu = cv2.moments(contours[1])
    cx = int(mu['m10']/mu['m00'])  #表示列数,是横坐标
    cy = int(mu['m01']/mu['m00'])

    #计算轮廓到重心的距离
    '''
    这里遇到一个问题:轮廓不总是一个像素点,也就是说轮廓是有厚度的
    '''
    label = np.where(drawing == 0)
    #注意此处矩阵元素的下标与在二维平面的坐标是反的,行表示纵坐标
    location = np.array(label).T
    location = location[:, ::-1]
    location = location.astype("int64")
    center = np.array([[cx, cy]])
    center = np.tile(center, (location.shape[0], 1))
    center = center.astype("int64")
    dist = (location - center)**2
    dist = np.sqrt(dist.sum(axis=1))

    up = location[:, 1] <= cy   #注意在重心以上,对应纵坐标是小
    down = location[:, 1] > cy
    location_up = location[up, :]
    location_down = location[down, :]
    dist_up = dist[up]
    dist_down = dist[down]
    angle_up = location_up[:, 0] - cx
    angle_up = np.arccos(angle_up / dist_up)
    angle_down = location_down[:, 0] - cx
    angle_down = np.arccos(angle_down / dist_down) * (-1) + np.pi * 2
    location = np.vstack((location_up, location_down))
    dist = np.hstack((dist_up, dist_down))
    angle = np.hstack((angle_up, angle_down))
    order = np.argsort(angle, axis=0)
    dist = dist[order]
    location = location[order]
    angle = angle[order]


    plt.figure()
    plt.plot(angle, dist / max(dist))
    plt.title(filename[i])
    plt.ylim(0, 1.2)
    plt.savefig(path_result + "/" + filename[i])
    plt.close()

    # 离散DCT
    time_domain = fft.dct(dist / max(dist))
    plt.figure()
    plt.plot(np.arange(0, 20, 1), np.abs(time_domain)[0:20])
    plt.xticks(np.arange(20))
    plt.title(filename[i])
    plt.savefig(path_result + "/ifft_" + filename[i])
    plt.close()

    # 差分
    diff_1 = np.gradient(dist / max(dist))
    diff_2 = np.gradient(dist / max(dist), 2)
    plt.figure()
    plt.plot(np.arange(0, diff_1.shape[0], 1), diff_1)
    plt.plot(np.arange(0, diff_2.shape[0], 1), diff_2)
    plt.title(filename[i])
    plt.legend(["order_1", "order_2"])
    plt.plot(np.arange(0, diff_2.shape[0], 1), 0*np.arange(0, diff_2.shape[0], 1))
    plt.savefig(path_result + "/diff_" + filename[i])
    plt.close()


    print(i)


    # cv2.circle(drawing, (cx, cy), 2, (0, 255, 255), -1)
    # cv2.imwrite(path_result + "/contour_" + filename[i], drawing)
    # cv2.waitKey(1000)
    # cv2.destroyAllWindows()

  • 26
    点赞
  • 189
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
你可以使用Python和OpenCV来进行颜色和形状识别。下面是一个基本的示例代码,演示了如何使用OpenCV识别物体的颜色和形状: ```python import cv2 import numpy as np # 定义要识别的颜色的范围 lower_blue = np.array([100, 50, 50]) upper_blue = np.array([130, 255, 255]) # 读取图像 image = cv2.imread('image.jpg') # 将图像从BGR转换为HSV颜色空间 hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 创建一个遮罩,将图像中在颜色范围内的部分标记为白色,其他部分标记为黑色 mask = cv2.inRange(hsv_image, lower_blue, upper_blue) # 执行形态学操作,去除噪点 kernel = np.ones((5, 5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 在原始图像上绘制识别出的物体的轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(image, contours, -1, (0, 255, 0), 3) # 显示结果图像 cv2.imshow('Color and Shape Detection', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个示例中,我们首先定义了要识别的颜色范围。然后,我们读取图像,并将其转换为HSV颜色空间。接下来,我们创建一个二值化的遮罩,将图像中在颜色范围内的部分标记为白色,其他部分标记为黑色。之后,我们执行形态学操作来去除噪点,并找到物体的轮廓。最后,我们在原始图像上绘制识别出的物体的轮廓,并显示结果图像。 请注意,这只是一个基本的示例代码,你可以根据实际情况进行调整和改进。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值