当前位置: 首页 > news >正文

OpenCV学习 day5

一、图像轮廓特征查找

图像轮廓特征查找其实就是他的外界轮廓

  • 应用:
    • 图像分割
    • 形状分析
    • 物体检测与识别

图像轮廓特征查找根据轮廓点进行,所以要先找到轮廓。

  • 对图像先灰度化、二值化。目标物体白色,非目标物体黑色,选择合适的二值化方法
  • 有了了轮廓点就可以找到最上、最下、最左、最右的四个坐标,就可以绘制矩形。

1、外接矩形(非旋转)

形状的外接矩形有两种,如下图,绿色的叫外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形。其中,外接矩形可根据获得到的轮廓坐标中最上、最下、最左、最右的点的坐标来绘制外接矩形,也就是下图中的绿色矩形。蓝色的是最小外接矩形,会考虑面积。
在这里插入图片描述
API及说明:

boundingRect(轮廓点)

作用:
用于计算轮廓的直立矩形(非旋转)的函数,其返回值是一个四元组 (x, y, w, h),数据类型为int,表示能完全包围轮廓的最小正矩形(无旋转)。

boundingRect函数,输入的是轮廓点,所以在运行过程中我们需要对图像的轮廓点进行查找检测、遍历提取,再将遍历的返回值输入进boundingRect函数

OpenCV代码实现:

import cv2 as cv
#读图
img = cv.imread("../../images/666.png")
img1 = cv.resize(img,(400,400))
# print(img.shape)
#灰度化
gray = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)
cv.imshow("gray", gray)#二值化
_,binary = cv.threshold(gray, 120, 255, cv.THRESH_OTSU + cv.THRESH_BINARY_INV)
cv.imshow("binary", binary)#查找轮廓
conts,hie = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)#轮廓特征查找,外接矩形 多条轮廓,遍历
for cont in conts:#x,y:外接矩形的左上角坐标;w,h:外接矩形的宽高#用于计算轮廓的直立矩形(非旋转)的函数,其返回值是一个四元组 (x, y, w, h),表示能完全包围轮廓的最小正矩形(无旋转)。x,y,w,h = cv.boundingRect(cont)print(x,y,w,h)#绘制外接矩形cv.rectangle(img1,(x,y),(x+w,y+h),(0,255,0),2)#显示效果
cv.imshow("contours", img1)
cv.waitKey(0)
cv.destroyAllWindows()

最终结果显示:
在这里插入图片描述

2、最小外接矩形

最小外接矩形是指能够完全包含给定二维形状(如点集、多边形等)且面积最小的矩形。这个概念在计算机图形学、地理信息系统(GIS)、模式识别等领域有广泛应用。

对于不规则图像,绘制的最小外接矩形通常是旋转的

计算方法,旋转卡壳法:

这是计算凸多边形最小外接矩形的经典算法:

  • 计算点集的凸包
  • 使用旋转卡壳技术,旋转矩形寻找最小面积

应用场景:

  • 物体识别和检测
  • 碰撞检测
  • 地理空间数据分析
  • 图像处理中的对象定位

API及说明:

contours,hierarchy=cv2.findContours(image_np_thresh,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours为二值图像上查找所有的外部轮廓

具体参数说明在OpenCV学习 day4中提及

rect = cv2.minAreaRect(cnt)

上一个API的返回值contours表示获取到的轮廓点的列表。将列表遍历后作为输入(cnt)传入minAreaRect函数,然后就可以计算出每个轮廓的最小外接矩形

  • rect 是计算轮廓最小面积外接矩形:返回值rect 结构通常包含中心点坐标 (x, y)、宽度 width、高度 height 和旋转角度 angle,也就是旋转矩形的数据元素

cv2.boxPoints(rect).astype(int)

cv2.boxPoints(rect)返回 是一个形状为 4行2列的数组,每一行代表一个点的坐标(x, y),也就是四个顶点坐标,顺序按照逆时针或顺时针方向排列

.astype(int)将浮点坐标转为整形(因像素坐标必须是整数)

最后一步:;连接四个顶点,在图像上绘制轮廓(绘制最小外接矩形)

cv2.drawContours(image, contours, contourIdx, color, thickness)

  • image:原图像,一般为numpy数组,通常为灰度图或彩色图像
  • contours:一个包含多个轮廓的列表,可以用上一个API得到的返回值,但必须使用[]中括号包裹,因为该函数的设计要求传入的是一个轮廓列表而不是单个轮廓(如果直接传入,会被误认欸多个轮廓点而非一个完整的轮廓)
  • contourldx:要绘制的轮廓索引。如果设置为-1,则绘制所有轮廓。
  • color:轮廓的颜色,可以是BGR颜色格式的三元组
  • thickness:轮廓线的粗细,如果是正数,则绘制实线;如果是0,则绘制轮廓点;如果是负数,则填充内部区域。

OpenCV代码实现:

import cv2 as cv
import numpy as np#读图
img = cv.imread("../../images/666.png")
img1 = cv.resize(img,(400,400))
# print(img.shape)
#灰度化
gray = cv.cvtColor(img1, cv.COLOR_BGR2GRAY)#二值化
_,binary = cv.threshold(gray, 120, 255, cv.THRESH_OTSU + cv.THRESH_BINARY_INV)#查找轮廓
conts,hie = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)#最小外接矩形
#遍历所有检测到的轮廓
for c in conts:'''计算轮廓的最小外接旋转矩形(RotatedRect)返回值rect是一个元组,包含:((矩形中心x, 中心y), (宽度, 高度), 旋转角度)注:角度范围[-90,0],宽度可能小于高度'''rect = cv.minAreaRect(c)'''将旋转矩形转换为4个顶点坐标boxPoints返回格式:[[x1,y1], [x2,y2], [x3,y3], [x4,y4]].astype(int)将浮点坐标转为整型(因像素坐标必须是整数)'''dst = cv.boxPoints(rect).astype(int)print(dst)#在图像img1上绘制轮廓  [dst] - 将顶点列表作为单个轮廓传入(注意外层要加[])cv.drawContours(img1, [dst], -1, (0,0,255), 2,cv.LINE_AA)cv.imshow("contours", img1)
cv.waitKey(0)
cv.destroyAllWindows()

最终结果显示:
在这里插入图片描述

3、最小外接圆

最小外接圆是指能够完全包围给定轮廓或点集的最小圆形。

寻找最小外接圆使用的算法是Welzl算法。Welzl算法基于一个定理:希尔伯特圆定理表明,对于平面上的任意三个不在同一直线上的点,存在一个唯一的圆同时通过这三个点,且该圆是最小面积的圆(即包含这三个点的圆中半径最小的圆,也称为最小覆盖圆)。

应用场景:

  • 目标尺寸测量:通过半径计算物体大小。
  • 圆形物体检测:如检测瞳孔、硬币、气泡。
  • 轮廓简化:用圆代替复杂轮廓进行碰撞检测。

与其他包围形状对比:

方法函数输出适用场景
凸包cv.convexHull()凸多边形点集轮廓凸性分析
直立矩形(AABB)cv.boundingRect()(x,y,w,h)快速区域提取
最小外接旋转矩形cv.minAreaRect()旋转矩形(中心、尺寸、角度)任意方向的长条形物体
最小外接圆cv.minEnclosingCircle()圆心 + 半径圆形物体

API及说明:

cv2.minEnclosingCircle(points) > 返回值 (center, radius)

  • points:输入目标图像的轮廓数据。通常遍历得到。
  • 返回值:
    • center:一个包含圆心坐标的二元组(x,y)
    • radius:浮点数类型,表示计算得到的最小覆盖圆的半径。

cv2.circle(img, center, radius, color, thickness)

  • img:输入图像,通常是一个numpy数组,表示要绘制圆形的图像。
  • center:一个二元组(x,y),表示圆心的坐标位置。
  • radius:整数或浮点型数值,表示圆的半径长度。
  • color:颜色标识,可以是BGR格式的三元组

OpenCV代码实现

import cv2 as cv
import numpy as np#读图 + 灰度化
num = cv.imread("../../images/num.png")
gray = cv.cvtColor(num, cv.COLOR_BGR2GRAY)#二值化
_,binary = cv.threshold(gray,0,255,cv.THRESH_OTSU + cv.THRESH_BINARY_INV)#查找坐标
counts,hie = cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)#遍历轮廓
for c in counts:#查找最小外接圆(x,y),r = cv.minEnclosingCircle(c)print(x,y,r)#数据类型转换x,y,r = np.int_(x),np.int_(y),np.int_(r)#绘制最小外接圆cv.circle(num,(x,y),r,(0,0,255),2,cv.LINE_AA)#显示效果
cv.imshow("num",num)
cv.waitKey(0)
cv.destroyAllWindows()

最终结果显示
在这里插入图片描述

二、直方图均衡化

1、什么是直方图

直方图是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。

基础概念:
直方图是图像像素强度的统计分布图,X轴表示像素值(0-255),Y轴表示该像素值出现的频率。

核心作用:

  • 分析图像对比度、亮度分布
  • 检测过曝/欠曝
  • 图像分割的前处理
  • 颜色分布分析(彩色图像)
  • 增加对比度:黑的更黑,白的更白。

2、绘制直方图

就是以像素值为横坐标,像素值的个数为纵坐标绘制一个统计图。

API及说明:

hist=cv2.calcHist(images, channels, mask, histSize, ranges)

  • images:输入图像列表,可以是一幅或多幅图像(通常是灰度图像或者彩色图像的各个通道)。
  • channels:一个包含整数的列表,指在每个图像上计算直方图的通道编号。如果输入的图像是灰度图,它的值就是[0];如果是彩色图像的话,传入的参数可以是[0],[1],[2]它们分别对应着通道B,G,R
  • mask(可选):一个与输入图像尺寸相同的二值掩膜图像,其中非零元素标记了参与直方图计算的区域,None代表全部计算。
  • histSize:一个整数列表,它是一个二维列表,每一维对应着一个通道的最小值和最大值,如灰度图就是[0,256]

返回值hist 是一个长度为255的数组,数组中的每个值表示图像中对应灰度等级的像素计数

minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)

获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索引

cv2.line(img, pt1, pt2, color, thickness)

  • img:原始图像,即要在上面画线的numpy数组(一般为uint8类型)。
  • pt1 和 pt2:分别为线段的起点和终点坐标,它们都是元组类型,例如 (x1, y1) 和 (x2, y2) 分别代表线段两端的横纵坐标。
  • color:线段的颜色,通常是一个包含三个元素的元组 (B, G, R) 表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。
  • thickness:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。

OpenCV代码实现

import cv2 as cv
import numpy as np#读图
cat = cv.imread("../../images/face.png")#统计像素
hist = cv.calcHist([cat],[0],None,histSize=[256],ranges=[0,256])
print(hist,len(hist))#获取直方图的最小值、最大值及其对应最 小值的位置索引、最大值的位置索引
minval, maxval , minloc, maxloc = cv.minMaxLoc(hist)
print(minval, maxval, minloc, maxloc)# 定义绘制图像的高
h_max = np.int_(400)#定义绘制的背景,黑色背景
back = np.zeros((400,256,3), np.uint8)#遍历
for i in range(256):#将统计频数转换为可视化的像素高度h = int(hist[i].item()*h_max/maxval)#绘制直方图cv.line(back,(i,400-h),(i,400),(0,255,0),1)cv.imshow("back",back)
cv.waitKey(0)
cv.destroyAllWindows()

3、直方图均衡化

一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。

如果一幅图像整体很亮,那所有的像素值的取值个数应该都会很高,所以应该把它的直方图做一个横向拉伸,就可以扩大图像像素值的分布范围,提高图像的对比度

通俗的讲,就是遍历图像的像素统计出灰度值的个数、比例与累计比例,并重新映射到0-255范围(也可以是其他范围)内,其实从观感上就可以发现,下面两幅图中前面那幅图对比度不高,偏灰白。

  • 直方图均衡化作用:
    • 增强对比度
    • 提高图像质量

在这里插入图片描述

(1)自适应直方图均衡化

自适应直方图均衡化(Adaptive Histogram Equalization, AHE),通过调整图像像素值的分布,使得图像的对比度和亮度得到改善。

API及说明:

dst = cv.equalizeHist(imgGray)

imgGray为需要直方图均质化的灰度图,返回值为处理后的图像

OpenCV代码实现:

import cv2 as cv#读图,灰度化
img = cv.imread("../../images/zhifang.png", cv.IMREAD_GRAYSCALE)
print(img.shape)
dst = cv.equalizeHist(img)cv.imshow("img", img)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()

结果显示
在这里插入图片描述

该方法适用于图像的灰度分布不均匀,且灰度分布集中在更窄的范围,图像的细节不够清晰且对比度较低的情况。

(2)对比度受限的自适应直方图均衡化

核心原理

与传统直方图均衡化的区别

特性传统均衡化CLAHE
处理范围全局图像局部区域(分块处理)
对比度控制无限制通过阈值限制对比度增强幅度
噪声抑制可能放大噪声有效抑制噪声
适用场景整体低对比度图像局部光照不均的图像(如医学影像)

API及说明:

clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)

  • clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
  • tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如(8, 8),表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。

创建CLAHE对象后,使用 .apply() 方法对图像进行CLAHE处理:

img=clahe.apply(image)

  • image:要均衡化的图像。
  • img均衡后的图像

OpenCV代码实现

import cv2 as cv#读图,灰度化
img = cv.imread("../../images/songyuqi.jpg")
img = cv.resize(img, dsize=(405, 721))
img1 = cv.cvtColor(img, cv.COLOR_BGR2GRAY)#创建clahe对象
clahe = cv.createCLAHE(clipLimit=2, tileGridSize=(8,8))#调用apply()方法
dst = clahe.apply(img1)#显示效果
cv.imshow("img", img)
cv.imshow("img1", img1)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()

结果显示

在这里插入图片描述

三、模板匹配

模板匹配就是用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。

  • 不会有边缘填充。
  • 类似于卷积,滑动比较,挨个比较象素。
  • 返回结果大小是:目标图大小-模板图大小-1。

基本原理:

  • 在目标图像中滑动搜索模板图像,计算相似度

  • 返回匹配位置坐标和置信度分数

关键特性:

特性说明
平移不变性只能检测平移变换,无法处理旋转/缩放
计算效率复杂度高(大图像+大模板时慢),适合小范围搜索
应用场景物体定位、工业质检、简单OCR

API及说明:

res=cv2.matchTemplate(image, templ, method)

  • image:原图像,这是一个灰度图像或彩色图像(在这种情况下,匹配将在每个通道上独立进行)。
  • templ:模板图像,也是灰度图像或与原图像相同通道数的彩色图像。
  • method:匹配方法,可以是以下之一:
    • cv2.TM_CCOEFF:需要先计算模板与目标图像的均值,然后通过每个像素与均值之间的差的乘积再求和来表示其匹配程度,1表示完美的匹配,-1表示最差的匹配。
    • cv2.TM_CCOEFF_NORMED:也是将相关系数匹配的结果统一到0到1之间,值越接近1代表匹配程度越高。
    • cv2.TM_CCORR:使用对应像素的乘积进行匹配,乘积的结果越大其匹配程度越高。
    • cv2.TM_CCORR_NORMED:与相关匹配类似,只不过是将其值统一到0到1之间,值越大,代表匹配程度越高。
    • cv2.TM_SQDIFF:以模板图与目标图所对应的像素值使用平方差公式来计算,其结果越小,代表匹配程度越高。
    • cv2.TM_SQDIFF_NORMED:与平方差匹配类似,只不过需要将值统一到0到1,计算结果越小,代表匹配程度越高。

这些方法决定了如何度量模板图像与原图像子窗口之间的相似度。

返回值res
函数在完成图像模板匹配后返回一个结果矩阵,这个矩阵的大小与原图像相同。矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度。
匹配方法不同,返回矩阵的值的含义也会有所区别。以下是几种常用的匹配方法及其返回值含义:

  1. cv2.TM_SQDIFF 或 cv2.TM_SQDIFF_NORMED:
    返回值越接近0,表示匹配程度越好。最小值对应的最佳匹配位置。
  2. cv2.TM_CCORR 或 cv2.TM_CCORR_NORMED:
    返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
  3. cv2.TM_CCOEFF 或 cv2.TM_CCOEFF_NORMED:
    返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。

绘制轮廓:
找的目标图像中匹配程度最高的点,我们可以设定一个匹配阈值来筛选出多个匹配程度高的区域。

loc=np.where(array > 0.8) #loc包含array中所有大于0.8的元素索引的数组

np.where(condition) 是 NumPy 的一个函数,当条件为真时,返回满足条件的元素的索引。

zip(*loc)

  • *loc 是解包操作,将 loc 中的多个数组拆开,作为单独的参数传递给 zip。
  • zip 将这些数组按元素一一配对,生成一个迭代器,每个元素是一个元组,表示一个坐标点。

OpenCV代码实现

import cv2 as cv
import numpy as np
#读图
game = cv.imread("../../images/game.png")
temp = cv.imread("../../images/temp.png")#转灰度
gray_game = cv.cvtColor(game, cv.COLOR_BGR2GRAY)
gray_temp = cv.cvtColor(temp, cv.COLOR_BGR2GRAY)
# print(gray_game.shape, gray_temp.shape)#获取模板图的宽高
h,w = gray_temp.shape#模板匹配 结果矩阵大小 = 目标 - 模板 + 1
ret = cv.matchTemplate(gray_game, gray_temp, cv.TM_CCOEFF_NORMED)
print(ret,ret.shape)#设定匹配阈值
threshold = 0.8
#获取结果矩阵中,匹配上的结果的位置索引
loc = np.where(ret > threshold)
# print(loc)#解包操作
iter = zip(*loc)for pt in iter:print(pt)right_bottom = (pt[1] + w , pt[0] + h)left_top = pt[::-1]cv.rectangle(game, left_top, right_bottom, (0, 255, 0), 1)#显示效果
cv.imshow("game", game)
cv.waitKey(0)
cv.destroyAllWindows()

结果显示

在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/316419.html

相关文章:

  • 从「同步」到「异步」:用 aiohttp 把 Python 网络 I/O 榨到极致
  • Python--OCR(2)
  • 微算法科技(NASDAQ:MLGO)基于量子重加密技术构建区块链数据共享解决方案
  • 算法438. 找到字符串中所有字母异位词
  • 算法第31天|动态规划:最后一块石头的重量Ⅱ、目标和、一和零
  • 二分查找
  • 算法训练营day41 动态规划⑧ 121. 122.123.买卖股票的最佳时机1.2.3
  • 常用技术资料链接
  • Spring小细节
  • oelove奥壹新版v11.7旗舰版婚恋系统微信原生小程序源码上架容易遇到的几个坑,避免遗漏参数白屏显示等问题
  • Electron-updater + Electron-builder + IIS + NSIS + Blockmap 完整增量更新方案
  • 物联网后端系统架构:从基础到AI驱动的未来 - 第十章:AI促进IOT领域发生革命式发展
  • WebRTC采集模块技术详解
  • 阿里云百炼平台创建智能体-上传文档
  • Mysql使用Canal服务同步数据->ElasticSearch
  • Linux-环境变量
  • Transformer的并行计算与长序列处理瓶颈
  • 视频转二维码在教育场景中的深度应用
  • QT跨线程阻塞调用方法总结
  • SpringMVC 6+源码分析(四)DispatcherServlet实例化流程 3--(HandlerAdapter初始化)
  • 【机器学习深度学习】 知识蒸馏
  • 2.4.9-2.5.1监控项目工作-控制质量-确认范围-结束项目或阶段
  • 三极管三种基本放大电路:共射、共集、共基放大电路
  • 后量子时代已至?中国量子加密技术突破与网络安全新基建
  • 无监督学习聚类方法——K-means 聚类及应用
  • CMAQ空气质量模式实践技术及案例分析应用;CMAQ空气质量模式配置、运行
  • Go语言实战案例:使用sync.Mutex实现资源加锁
  • 一次完整的 Docker 启动失败排错之旅:从 `start-limit` 到 `network not found
  • 三坐标测量机全自研扫描测头+标配高端性能,铸就坚实技术根基
  • 如何实现一个简单的基于Spring Boot的用户权限管理系统?