OpenCV-Python 通过边缘检测识别物体并批量提取(大米识别为例)——minAreaRect批量生成物体的最小外接矩形(旋转矩形)并批量裁剪

2023-11-12

OpenCV版本:4.0.0.21(已兼容4.5.2.X版本)

算法实现思路如下:

  1. 对图像做降噪滤波处理
  2. 提取边缘
  3. 检测轮廓
  4. 检测轮廓最小外接矩形(旋转矩形)
  5. 旋转图像
  6. 裁剪

代码如下:

import cv2
import numpy as np

image = cv2.imread("rice.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 转为灰度图
blurred = cv2.GaussianBlur(gray, (11, 11), 0)
edge = cv2.Canny(blurred, 30, 150)  # 用Canny算子提取边缘
cv2.imwrite("./results/edge.jpg", edge)

contour = image.copy()
(cnts, _) = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测
cv2.drawContours(contour, cnts, -1, (0, 255, 0), 2)  # 绘制轮廓
cv2.imwrite("./results/contour.jpg", contour)

count = 0  # 米粒个数
margin = 5  # 裁剪边距
draw_rect = image.copy()
for i, contour in enumerate(cnts):
    area = cv2.contourArea(contour)  # 计算包围形状的面积
    if area < 15:  # 过滤面积小于15的形状
        continue
    count += 1
    rect = cv2.minAreaRect(contour)  # 检测轮廓最小外接矩形,得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
    box = np.int0(cv2.boxPoints(rect))  # 获取最小外接矩形的4个顶点坐标
    cv2.drawContours(draw_rect, [box], 0, (255, 0, 0), 2)  # 绘制轮廓最小外接矩形

    h, w = image.shape[:2]  # 原图像的高和宽
    rect_w, rect_h = int(rect[1][0]) + 1, int(rect[1][1]) + 1  # 最小外接矩形的宽和高
    if rect_w <= rect_h:
        x, y = int(box[1][0]), int(box[1][1])  # 旋转中心
        M2 = cv2.getRotationMatrix2D((x, y), rect[2], 1)
        rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
        y1, y2 = y - margin if y - margin > 0 else 0, y + rect_h + margin + 1
        x1, x2 = x - margin if x - margin > 0 else 0, x + rect_w + margin + 1
        rotated_canvas = rotated_image[y1: y2, x1: x2]
    else:
        x, y = int(box[2][0]), int(box[2][1])  # 旋转中心
        M2 = cv2.getRotationMatrix2D((x, y), rect[2] + 90, 1)
        rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
        y1, y2 = y - margin if y - margin > 0 else 0, y + rect_w + margin + 1
        x1, x2 = x - margin if x - margin > 0 else 0, x + rect_h + margin + 1
        rotated_canvas = rotated_image[y1: y2, x1: x2]
    print("rice #{}".format(count))
    # cv2.imshow("rotated_canvas", rotated_canvas)
    cv2.imwrite("./rotation-results/{}.jpg".format(count), rotated_canvas)
    cv2.waitKey(0)
cv2.imwrite("./results/rect.jpg", draw_rect)

        原图像如下:

        裁剪出来的部分效果如下:

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 转为灰度图
blurred = cv2.GaussianBlur(gray, (11, 11), 0)
edge = cv2.Canny(blurred, 30, 150)  # 用Canny算子提取边缘
cv2.imwrite("./results/edge.jpg", edge)

        将读取的图像转为灰度图,然后做高斯降噪滤波处理,再用Canny算子检测边缘转换为二值图,效果如下:

contour = image.copy()
(cnts, _) = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测
cv2.drawContours(contour, cnts, -1, (0, 255, 0), 2)  # 绘制轮廓
cv2.imwrite("./results/contour.jpg", contour)

        cv2.findContours()函数可以用来检测轮廓。它的第一个参数是要检测的图像,必须是二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。第二个参数有四种,表示轮廓的检索模式:

  • cv2.RETR_EXTERNAL表示只检测外轮廓
  • cv2.RETR_LIST检测的轮廓不建立等级关系
  • cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层
  • cv2.RETR_TREE建立一个等级树结构的轮廓

        第三个参数为轮廓的近似办法:

  • cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
  • cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

        cv2.findContours()函数有两个返回值,一个是轮廓本身,还有一个是每条轮廓对应的属性

  1. contour返回值:第一个值返回的是list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示。每一个ndarray里保存的是轮廓上的各个点的坐标。
  2. hierarchy返回值:此外,该函数还可返回一个可选的hiararchy结果,这是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。

        cv2.drawContours()函数可以在图像上绘制轮廓:

  1. 第一个参数是指明在哪幅图像上绘制轮廓
  2. 第二个参数是轮廓本身,在Python中是一个list
  3. 第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓
  4. 第四个参数是轮廓线条的颜色
  5. 第五个参数是轮廓线条的粗细

        绘制轮廓效果如下:

for i, contour in enumerate(cnts):
    ares = cv2.contourArea(contour)  # 计算包围形状的面积
    print(ares)
    if ares < 15:  # 过滤面积小于15的形状
        continue
    count += 1

        因为cv2.findContours()返回的第一个值是listlist中每个元素都是图像中的一个轮廓,所以就可以用for循环对返回值进行遍历(批量处理)。cv2.contourArea()可以计算包围形状的面积,当检测的轮廓是个很小的点或者不规则时,就可以设置包围形状面积的大小过滤掉(这个函数计算的面积有时候不太准确,使用须谨慎)。

rect = cv2.minAreaRect(contour)  # 检测轮廓最小外接矩形,得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
box = np.int0(cv2.boxPoints(rect))  # 获取最小外接矩形的4个顶点坐标
cv2.drawContours(image, [box], 0, (255, 0, 0), 2)  # 绘制轮廓最小外接矩形

        cv2.minAreaRect()可以得到最小面积的矩形,这个矩形是可以有偏转角度的,可以与图像的边界不平行。返回一个Box2D结构rect:(最小外接矩形的中心(x,y),(宽,高),旋转角度θ),但是要绘制这个矩形,需要通过函数cv2.boxPoints()获得矩形的顶点坐标box,返回形式[ [x0,y0], [x1,y1], [x2,y2], [x3,y3] ]。用cv2.drawContours()绘制出来的效果如下:

        这里说一下cv2.minAreaRect()cv2.boundingRect()的区别。cv2.boundingRect()得到的是包覆轮廓的最小正矩形,cv2.minAreaRect()得到的是包覆轮廓的最小外接矩形。下图蓝色框是最小外接矩形,红色框是最小正矩形

        裁剪最小外接矩形(旋转矩形)通常的做法是通过旋转,将矩形的四个坐标点做映射,然后求出被旋转后图像的四个点的坐标再裁剪,这个方法需要通过坐标变换求出一系列的参数似乎有点麻烦。现在来看看旋转函数cv2.getRotationMatrix2D()的参数:

  1. 旋转时固定的点

  2. 旋转角度

  3. 图片缩放尺度

        那么,根据cv2.getRotationMatrix2D()的第一个参数旋转时固定的点(即旋转中心)还有cv2.boxPoints()返回矩形的四个顶点坐标,就可以以矩形的其中一个顶点坐标作为旋转中心进行旋转,然后再根据cv2.minAreaRect()函数返回的参数((宽,高),旋转角度θ)在旋转中心的坐标上加上矩形的宽和高就可以进行裁剪。(注意:这里裁剪是以矩形的左上顶点进行裁剪)

h, w = image.shape[:2]  # 原图像的长和宽
rect_w, rect_h = int(rect[1][0]) + 1, int(rect[1][1]) + 1  # 最小外接矩形的宽和高
if rect_w <= rect_h:
    x, y = int(box[1][0]), int(box[1][1])  # 旋转中心
    M2 = cv2.getRotationMatrix2D((x, y), rect[2], 1)
    rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
    y1, y2 = y - margin if y - margin > 0 else 0, y + rect_h + margin + 1
    x1, x2 = x - margin if x - margin > 0 else 0, x + rect_w + margin + 1
    rotated_canvas = rotated_image[y1: y2, x1: x2]

        因为裁剪需要的参数是整数,所以rect_wrect_h这里设置为int类型,而int是向下取整,所以需要加1,不然裁剪的时候会损失精度。矩形旋转为竖直的时候,需要分两种情况来讨论,一是当矩形的宽<=矩形的高时,旋转矩形顺时针旋转旋转角度θ为负),示意图如下:

        cv2.getRotationMatrix2D()返回的是个矩阵,通过cv2.warpAffine()仿射变换完成旋转,其中cv2.warpAffine()的第三个参数设置为原图的2倍,是为了防止旋转之后目标区域变换到图像外面。裁剪的时候再加1是因为切片符号取不到右边的值,所以加1

 else:
    x, y = int(box[2][0]), int(box[2][1])  # 旋转中心
    M2 = cv2.getRotationMatrix2D((x, y), rect[2] + 90, 1)
    rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
    y1, y2 = y - margin if y - margin > 0 else 0, y + rect_w + margin + 1
    x1, x2 = x - margin if x - margin > 0 else 0, x + rect_h + margin + 1
    rotated_canvas = rotated_image[y1: y2, x1: x2]

        矩形旋转为竖直的第二种情况是,当矩形的宽>矩形的高时,旋转矩形逆时针旋转旋转角度θ为负),示意图如下:

        当矩形的宽>矩形的高时,旋转矩形按旋转角度θ顺时针旋转为图中的绿色矩形,因为这里裁剪是以矩形的左上顶点进行裁剪,所以还需要加一个90°进行逆时针旋转红色矩形,最后旋转矩形蓝色)到正矩形红色)整体看起来就是逆时针旋转

        注意:

  • 旋转角度θ是水平轴(x轴)逆时针旋转到矩形的第一条边的夹角,这个边是width,另一条边是height。这里的width与height是相对的。(特殊情况:正矩形的旋转角度θ为-0)

参考链接:

OpenCV 识别图片中的米粒个数,并计算米粒的平均面积和长度_逗创科技——逗创创的博客-CSDN博客

对硬币、销钉、大米进行图像分割(附代码)_小小何先生的学习之旅-CSDN博客

使用OpenCV-python提取图片中的硬币_凯哥大数据——刘凯的博客-CSDN博客

python利用四个坐标点对图片目标区域最小外接矩形进行裁剪_u014662865的博客-CSDN博客

使用Python和OpenCV检测图像中的物体并将物体裁剪下来_liqiancao的专栏-CSDN博客

python opencv minAreaRect 生成最小外接矩形_lanyuelvyun的博客-CSDN博客_cv2.minarearect

OpenCV 中boundingRect、minAreaRect、minEnclosingCircle用法_线上幽灵-CSDN博客_cv2.minenclosingcircle

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV-Python 通过边缘检测识别物体并批量提取(大米识别为例)——minAreaRect批量生成物体的最小外接矩形(旋转矩形)并批量裁剪 的相关文章

随机推荐

  • 如何在IDEA中创建Web项目

    棒棒有言 也许我一直照着别人的方向飞 可是这次 我想要用我的方式飞翔一次 人生 既要淡 又要有味 凡事不必太在意 一切随缘 缘深多聚聚 缘浅随它去 凡事看淡点看开些 顺其自然 无意于得 就无所谓失 人生 看轻看淡多少 痛苦就远离你多少 本章
  • 强化学习12——动态规划与策略迭代和值迭代

    上一节我们说了马尔可夫决策过程 它是对完全可观测的环境进行描述的 也就是观测到的内容完整决定了决策所需要的特征 马尔可夫决策过程可以用方程组求解简单问题 但是对于复杂一点的问题 一般通过迭代的思想对其进行求解 动态规划是非常有效的求解马尔可
  • 计算着色器中线程的编号

    ID3D11DeviceContext Dispatch Method Execute a command list from a thread group Syntax void Dispatch in UINT ThreadGroupC
  • Rstudio与R的绑定和更新

    遇到新安装的R与Rstudio没有匹配或者想要更改R的版本 一些老的版本的安装包在新版R上可能不适用 可以如下操作 一 Rstudio与R的绑定 1 选择Tools里的Global Options选项 2 选择General更改R vers
  • webpack配置本地TypeScript编译环境和开启本地服务

    目录 1 创建一个文件夹 2 初始化一个package json文件对我们安装包进行记录 3 安装webpack 4 配置webpack config js文件 1 创建一个文件夹 2 初始化一个package json文件对我们安装包进行
  • pip安装pandas总是失败的解决办法

    先卸载numpy和pandas pip uninstall numpy pip uninstall pandas 再重新安装 按numpy pandas的顺序 1 pip install numpy i https pypi douban
  • CXF开发WebService客户端

    开发必备 1 apache cxf 2 2 6 2 spring ws 1 5 8 3 eclipse jee galileo SR1 win32 开发步骤 一 新建一个普通的java工程 名字叫WebService CXF Client
  • Leetcode刷题48-575. 分糖果(C++详细解法!!!)

    题目来源 链接 https leetcode cn com problems distribute candies 575 分糖果 1 问题描述 2 我的解决方案 3 大神们的解决方案 4 我的收获 4 1 set的用法 4 2 map容器
  • ubuntu 安装Android SDK,如何在Ubuntu12.1下安装Android SDK

    如何在Ubuntu12 1下安装Android SDK 在Ubuntu下 安装 Android SDK 有两种方式 1 为使用ADT Eclipse安装 这也是官方推荐的方式 适合使用Eclipse开发的开发者 2 直接安装Android
  • 跨行业数据挖掘过程标准及其在 Python 中的应用

    跨行业数据挖掘过程标准及其在 Python 中的应用 数据挖掘是一种从大量数据中提取有用信息的过程 它在各个行业中都有广泛的应用 为了规范数据挖掘过程 CRISP DM Cross Industry Standard Process for
  • PPP与PPPoe

    PPP 二层最常用的封装技术 PPP共定义了三个协议组件 分别是数据封装方式 链路控制协议 Link Control Protocol LCP 和网络层控制协议 Network Control Protocol NCP 数据封装方式定义了如
  • 详谈概率图模型(PGM)

    概率图 引言 1 概述 2 基本问题 2 1 模型表示 2 1 1 有向图模型 2 1 2 无向图模型 2 1 3 有向图和无向图之间的转化 2 2 学习问题 2 3 推断 引言 quad quad 机器学习是根据一些已观察到的证据 如训练
  • VMware vCenter 7.0U2A升级攻略VMware vCenter 7.0U3C攻略

    一 概述 写这篇文章是因为VMware 7 0U2A之前版本有个bug 用户可根据漏洞 CVE 2021 22005 进行提权443端口 直接访问vCenter 443管理界面 然后想着将VMware 7 0U2A升级至最新版VMware
  • 应急响应思路

    应急响应过程 目的 分析攻击时间 攻击操作 攻击结果 安全修复等并给出合理的解决方案 保护阶段 直接断网 保护现场 看是否能够恢复数据 分析阶段 对入侵过程进行分析 常见方法为指纹库搜索 日志时间分析 后门追查分析 漏洞检查分析等 复现阶段
  • AttributeError: module ‘seaborn‘ has no attribute ‘scatterplot‘

    我找到很多博客 说这是因为seaborn的版本不对 我就去升级seaborn了 发现仍然是报这个错误 经过排查 pip list发现里面有个叫做sns的包 版本是0 1 我是import seaborn as sns了 并不清楚这个sns包
  • 虚机固定IP---小黑日常超细教程

    前言 虚机重启后的ip段不变但是最后的数字有时候是随机的 如需要固定环境的ip则需要更改虚机的配置文件 目录 一 更改配置文件 二 查看自己虚机的网关 三 配置网络工作 四 配置公告的DNS服务 五 关闭防火墙 重启网络服务 一 更改配置文
  • 使用memtier_benchmark工具对Redis进行测试记录-20230330

    文章目录 一 概述 一 安装相关软件 1 安装redis server 2 安装memtier benchmark软件 3 redis的一些基本操作 二 测试步骤 1 准备redis数据 2 启动进程进行测试 3 参数含义解释 三 创建多个
  • 【华为OD机试 2023 B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • 深度相机Kinect2.0三维点云拼接实验(一)

    文章目录 摘要 Kinect2 0简介 工作原理 RGB相机成像原理 深度相机成像原理 总结 参考文献 摘要 Kinect2 0是微软推出的一款RGB D相机 它即支持普通相机的拍摄 也支持脉冲测量深度信息 本系列文章基于该传感器给出基本的
  • OpenCV-Python 通过边缘检测识别物体并批量提取(大米识别为例)——minAreaRect批量生成物体的最小外接矩形(旋转矩形)并批量裁剪

    OpenCV版本 4 0 0 21 已兼容4 5 2 X版本 算法实现思路如下 对图像做降噪滤波处理 提取边缘 检测轮廓 检测轮廓最小外接矩形 旋转矩形 旋转图像 裁剪 代码如下 import cv2 import numpy as np