在统计学中,直方图是一种对数据分布情况的图形表示,是一种二维统计图表
图像直方图是用一表示数字中亮度分布的直方图,标绘了图像中每个亮度值的像素数,可以借助观察该直方图了需要如何调整亮度分布的直方图。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分,而整体明亮,只有少量明显的图像则相反
语法:calcHist(imgs, channels, mask, histSize, ranges[, hist[, accumulate]])
参数:
imgs
:图像集,传入一个图像列表
channels
:指定通道
需要用中括号括起来,输入图像是灰度图像的值是[0],彩色图像可以是[0], [1], [2],分别对应B, G, R
mask
:掩码图像
histSize
:BINS的数量
需要用中括号括起来,例如[256]
ranges
:像素值范围,例如[0, 255]
accumulate
:累积标识
# !/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo9.py"
__time__ = "2022/7/18 18:09"
import cv2.cv2 as cv2
import numpy as np
# 读取图像
img = cv2.imread("./img/1.jpg")
assert isinstance(img, np.ndarray)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
print(hist.shape)
# 如何绘制直方图
# !/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo9.py"
__time__ = "2022/7/18 18:09"
import cv2.cv2 as cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread("./img/1.jpg", cv2.IMREAD_GRAYSCALE) # 读取黑白效果
assert isinstance(img, np.ndarray)
# print(img.ravel())
# 统计直方图数据
plt.hist(img.ravel(), 256, [0, 255]) # 转换为一维数据,
plt.show() # 绘制直方图数据
# !/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo9.py"
__time__ = "2022/7/18 18:09"
import cv2.cv2 as cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread("./img/1.jpg") # 读取黑白效果
assert isinstance(img, np.ndarray)
# 统计直方图数据
histB = cv2.calcHist([img], [0], None, [256], [0, 255]) # 蓝色通道
histG = cv2.calcHist([img], [1], None, [256], [0, 255]) # 绿色通道
histR = cv2.calcHist([img], [2], None, [256], [0, 255]) # 红色通道
# 画图
plt.plot(histB, color="b", label="blue")
plt.plot(histG, color="g", label="green")
plt.plot(histR, color="r", label="red")
plt.legend()
plt.show()
如何生成掩膜:
mask = np.zeros(img.shape, np.uint8)
mask[100: 200, 200: 300] = 255
# !/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo9.py"
__time__ = "2022/7/18 18:09"
import cv2.cv2 as cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread("./img/1.jpg", cv2.IMREAD_GRAYSCALE) # 读取黑白效果
assert isinstance(img, np.ndarray)
# 生成掩膜
mask = np.zeros(img.shape, np.uint8)
mask[100:700, 200: 900] = 255
# # 进行与操作:img和img做与运算,结果再和mask做与运算
# new_img = cv2.bitwise_and(img, img, mask=mask)
# 掩膜直方图,专门对某一个区域
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 255])
hist_gray = cv2.calcHist([img], [0], None, [256], [0, 255])
# 统计结果
plt.plot(hist_mask, label="mask")
plt.plot(hist_gray, label="gray")
plt.legend()
plt.show()
cv2.VideoCapture
可以捕获摄像头,用数字来表示不同的设备,比如0,1#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import cv2.cv2 as cv
# 打开视频文件
vc = cv.VideoCapture("./img/1.mp4")
# 打开摄像头
vc_ = cv.VideoCapture(0)
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import cv2.cv2 as cv
# 打开摄像头,并读取数据
vc = cv.VideoCapture(0) # 如果打开失败,或者是没有读取到数据,不会报错
while vc.isOpened(): # 如果摄像头成功打开
# 读每一帧数据,返回标记,True表示读到了数据,False表示是没有读到数据
flag, frame = vc.read()
if not flag:
break # 如果没有读到数据,直接退出
# 显示数据
cv.imshow("video", frame)
key = cv.waitKey(30) # 注意,不能写0,不然要点退出才能展示下一帧的数据,每隔10毫秒显示数据
if key & 0xFF == ord('q'): # 使用 key 来监控键盘中输入的内容
break
# 别忘了释放资源
vc.release()
cv.destroyAllWindows()
参数 | propld | 功能 |
---|---|---|
cv2.CAP_PROP_POS_MSEC | 0 | 视频文件的当前位置(以毫秒为单位)或视频捕获时间戳 |
cv2.CAP_PROP_POS_FRAMES | 1 | 基于0的索引将被解码/捕获下一帧 |
cv2.CAP_PROP_POS_AVI_RATIO | 2 | 视频文件的相对位置:0 - 视频的开始,1 - 视频的结束 |
cv2.CAP_PROP_FRAME_WIDTH | 3 | 帧的宽度 |
cv2.CAP_PROP_FRAME_HEIGHT | 4 | 帧的高度 |
cv2.CAP_PROP_FPS | 5 | 帧速 |
cv2.CAP_PROP_FOURCC | 6 | 4个字符表示的视频编码器格式 |
cv2.CAP_PROP_FRAME_COUNT | 7 | 帧数 |
cv2.CAP_PROP_FORMAT | 8 | byretrieve()返回的Mat对象的格式 |
cv2.CAP_PROP_MODE | 9 | 指示当前捕获模式的后端特定值 |
cv2.CAP_PROP_BRIGHTNESS | 10 | 图像的亮度(仅适用于相机) |
cv2.CAP_PROP_CONTRAST | 11 | 图像对比度(仅适用于相机) |
cv2.CAP_PROP_SATURATION | 12 | 图像的饱和度(仅适用于相机) |
cv2.CAP_PROP_HUE | 13 | 图像的色相(仅适用于相机) |
cv2.CAP_PROP_GAIN | 14 | 图像的增益(仅适用于相机) |
cv2.CAP_PROP_EXPOSURE | 15 | 曝光(仅适用于相机) |
cv2.CAP_PROP_CONVERT_RGB | 16 | 表示图像是否应转换为RGB的布尔标志 |
cv2.CAP_PROP_WHITE_BALANCE | 17 | |
cv2.CAP_PROP_RECTIFICATION | 18 | 立体摄像机的整流标志 |
获取和修改属性值:
vc = cv.VideoCapture(0) # 如果打开失败,或者是没有读取到数据,不会报错
vc.set(propId, value) # 设置视频的属性值
"""
:propId: 上面表格中的id值
:value: 修改后的值
"""
vc.get(propId) # 获取对应功能的值
cv2.VideoWriter()
VideoWriter_fourcc
)write编码,并写入缓存
release函数将缓存内容写入磁盘,并释放资源
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import cv2.cv2 as cv
# 打开摄像头,并读取数据
cap = cv.VideoCapture(0)
fourcc = cv.VideoWriter_fourcc(*'mp4v') # 文件格式,*表示解包操作
vw = cv.VideoWriter("./img/outer.mp4", fourcc, 30, (640, 480)) # (640, 480) 表示摄像头拍摄视频,这个大小不能搞错
cap.set(cv.CAP_PROP_FPS, 30) # 设置摄像头的帧率
while cap.isOpened(): # 根据flag来判断是否读到了视频数据
# 读每一帧数据,返回标记,True表示读到了数据,False表示是没有读到数据
flag, frame = cap.read()
if not flag:
print("没有捕获到视频数据")
break # 如果没有读到数据,直接退出
# 将数据写入文件中
vw.write(frame)
cv.imshow("frame", frame)
if cv.waitKey(1000 // 30) == ord('q'):
break
vw.release() # 将数据内容写入文件中
cap.release() # 并释放缓存内容
cv.destroyAllWindows() # 摧毁窗口
VideoWriter_fourcc
:
CV_FOURCC('I','4','2','0') :未压缩的YUV编码,4:2:0色度子采样。这种编码广泛兼容,但会产生大文件。文件扩展名应为.avi CV_FOURCC('P','I','M','1') :MPEG-1编码。文件扩展名应为.avi。 CV_FOURCC('X','V','I','D') :相对较旧的MPEG-4编码。如果要限制结果视频的大小,这是一个很好的选择。文件扩展名应为.avi。 CV_FOURCC('M','P','4','V') :另一个相对较旧的MPEG-4编码。如果要限制结果视频的大小,这是一个很好的选择。文件扩展名应为.mp4 CV_FOURCC('X','2','6','4'): 一种比较新的MPEG-4编码方式。如果你想限制结果视频的大小,这可能是最好的选择。文件扩展名应为.mp4 CV_FOURCC('F','L','V','1') :此选项为Flash视频。文件扩展名应为.flv
OpenCV 允许我们对窗口上的鼠标动作做出响应
setMouseCallbask(winname, callback, userdata)
winname
:窗口名字callback
:回调函数userdata
:给回调函数传递的参数callback(event, x, y, flags, userdata)
event
:鼠标事件x, y
:坐标点flags
:主要用于组合键userdata
:给回调函数传递的参数鼠标事件:
EVENT_MOUSEMOVE 0 # 滑动
EVENT_LBUTTONDOWN 1 # 左键点击
EVENT_RBUTTONDOWN 2 # 右键点击
EVENT_MBUTTONDOWN 3 # 中键点击
EVENT_LBUTTONUP 4 # 左键放开
EVENT_RBUTTONUP 5 # 右键放开
EVENT_MBUTTONUP 6 # 中键放开
EVENT_LBUTTONDBLCLK 7 # 左键双击
EVENT_RBUTTONDBLCLK 8 # 右键双击
EVENT_MBUTTONDBLCLK 9 # 中键双击
"""组合键"""
EVENT_FLAG_LBUTTON 1 # 左键拖曳
EVENT_FLAG_RBUTTON 2 # 右键拖曳
EVENT_FLAG_MBUTTON 4 # 中键拖曳
EVENT_FLAG_CTRLKEY 8 # (8~15)按Ctrl不放事件
EVENT_FLAG_SHIFTKEY 16 # (16~31)按Shift不放事件
EVENT_FLAG_ALTKEY 32 # (32~39)按Alt不放事件
示例:
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import numpy as np
import cv2.cv2 as cv
# 回调函数函数名可以随便取
def callback_mouse(event, x, y, flags, userdata):
"""
用来监听鼠标事件的函数
:param event: 鼠标事件
:param x: x坐标
:param y: y坐标
:param flags: 用于组合键
:param userdata: 传入的数据
:return: None
"""
print(event, x, y, flags, userdata)
# 创建窗口
cv.namedWindow("mouse", cv.WINDOW_NORMAL)
cv.resizeWindow("mouse", 640, 360)
# 设置鼠标回调函数
cv.setMouseCallback("mouse", callback_mouse, "这个为传入的用户数据") # 给mouse窗口添加回调函数
img = np.zeros((360, 640, 3), np.uint8)
while True:
cv.imshow("mouse", img)
if cv.waitKey(1) == ord("q"):
break
cv.destroyAllWindows()
特征检测是计算机视觉和图像处理中的一种概念,它指的是使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征。特征检测的结果是把图像上的点分为不同的子集,这些子集往往属于孤立的点,连续的曲线或者连续的区域
特征检测包括边缘检测、角检测、区域检测和脊检测
特征检测应用场景:
平坦部分很难找到它在原图中的位置
边缘相对于平坦要好找一些,但是也不能立马确定
角点可以马上找到其在原图中的位置
图像特征就是值有意义的图像区域,具有独特性,易于识别,比较角点,斑点及高密度区
在图像特征中最重要的就是角点,哪些是角点呢?
人眼对角点的识别通常是在一个局部的小区域或小窗口完成的。如果在各个方向上移动这个特征的小窗口,窗口内区域的灰度发生了较大的变化,那么就认为在窗口内遇到了角点。如果这个特定的窗口在图像各个方向上移动时,窗口内图像的灰度没有发生变化,那么窗口内就不存在角点;如果窗口在某一个方向移动时,窗口内图像的灰度发生了较大的变化,而在另一些方向上没有发生变化,那么,窗口内的图像可能就是一条直线的线段。
对于图像I(x,y)I(x,y),当在点(x,y)(x,y)处平移(Δx,Δy)(Δx,Δy)后的自相似性,可以通过自相关函数给出:
W(x,y)W(x,y)是以点(x, y)(x, y)为中心的窗口,即加权函数,例如高斯加权函数
基于泰勒展开式,对图像(x, y)I(x, y)在平移(Δx,Δy)(Δx,Δy)后进行一阶近似
其中,lx, lylx, ly 是图像I(x,y)I(x,y)的偏导数
近似可得:
其中M:
化简可得:
二次项函数本质上就是一个椭圆函数,椭圆方程为:
椭圆函数特征值与图像中的角点、直线(边缘)和平面之间的关系如下图所示。共可分为三种情况:
根据二次项函数特征值的计算公式,我们可以求M(x,y)M(x,y)矩阵的特征值。但是Harris给出的角点差别方法并不需要计算具体的特征值,而是计算一个角点响应值RR来判断角点。RR的计算公式为:
R = d e t M − α ( t r a c e M ) 2 d e t M = λ 1 λ 2 = A C − B 2 t r a c e M = λ 2 + λ 2 = A + C R=detM−α(traceM)^2\\ detM=\lambda 1λ2=AC−B^2\\ traceM=λ2+λ2=A+C R=detM−α(traceM)2detM=λ1λ2=AC−B2traceM=λ2+λ2=A+C
语法:cornerHarris(src, blockSize, ksize, k[, dist[, borderType]])
参数:
blockSize
:检测窗口大小ksize
:sobel的卷积核k
:权重系数,即上面公式中的 α \alpha α,是一个经验值,一般取0.04~0.06之间,一般默认0.04#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import cv2.cv2 as cv2
# 读取图片
img = cv2.imread("./img/1.jpg")
# 转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 角点检测
# blockSize 没有要求是必须奇数的,返回角点响应,R值
dst = cv2.cornerHarris(img_gray, 2, 3, 0.04)
# 显示角点
# 设定阈值:dst > 0.01 * dst.max()(经验值)
img[dst > (0.01 * dst.max())] = [0, 0, 255] # 找到这些角点,并且将角点的颜色改为红色
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
SIFT,即尺度不变换特征转换,适用于图像处理领域的一种描述,这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子
Harris角点具有旋转不变的特性,但是缩放后,原来的角点有可能不就是角点了
算法原理:原理中公式太多,观摩大佬的文章
使用算法的步骤:
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(img, ...)
cv2.drawKeypoints(gray, kp, img)
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import cv2.cv2 as cv2
# 读取图片
img = cv2.imread("./img/1.jpg")
# 转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 注意:xfeatures2d是OpenCV的扩展包,需要安装opencv-contrib-python才有
# 创建sift对象
sift = cv2.xfeatures2d.SIFT_create() # type: cv2.SIFT
# 进行检测
kp = sift.detect(img_gray) # 是一个列表,里面存放的是封装的KeyPoint对象
# 绘制关键点
cv2.drawKeypoints(img_gray, kp, img) # 创建角点的对象 角点列表 输出图像
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键点和描述子:
- 关键点:位置、大小和方向
- 描述子:记录了关键点周围对其有共享的像素点的一组向量值,其不受仿射变换,光照变换等影响。描述子的作用就是特征匹配,在后面进行特征匹配的时候会用上
#!/usr/bin/python3 # -*- coding: UTF-8 -*- __author__ = "A.L.Kun" __file__ = "demo10.py" __time__ = "2022/7/29 19:20" import cv2.cv2 as cv2 # 读取图片 img = cv2.imread("./img/1.jpg") # 转换为灰度图 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 注意:xfeatures2d是OpenCV的扩展包,需要安装opencv-contrib-python才有 # 创建sift对象 sift = cv2.xfeatures2d.SIFT_create() # type: cv2.SIFT # 进行检测 # kp = sift.detect(img_gray) # 是一个列表,里面存放的是封装的KeyPoint对象 # 检测关键点,并计算描述子 # kp, des = sift.compute(img, kp) # 或者一步到位,把关键点和描述子一起检测出来 kp, des = sift.detectAndCompute(img_gray, None) # 绘制关键点 cv2.drawKeypoints(img_gray, kp, img) # 创建角点的对象 角点列表 输出图像 cv2.imshow("img", img) cv2.waitKey(0) cv2.destroyAllWindows()
语法:goodFeaturesToTrack(img, maxComers, qualityLevel, minDistance[, corners[, mask[, blockSize[, userHarrisDetect[, k]]]]])
maxCorner
:角点的最大数,值为0表示无限制qualityLevel
:角点质量,小于1.0的整数,一般在0.01~0.1之间minDiatance
:教之间最小欧氏距离,忽略小于此距离的点mask
:掩膜blockSize
:检测窗口的大小userHarris
:是否使用Harris算法k
:默认是0.04#!/usr/bin/python3
# -*- coding: UTF-8 -*-
__author__ = "A.L.Kun"
__file__ = "demo10.py"
__time__ = "2022/7/29 19:20"
import numpy as np
import cv2.cv2 as cv2
# 读取图片
img = cv2.imread("./img/1.jpg")
# 转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(img_gray, 100, 0.01, 10) # 最大找出100个角点
corners = np.int0(corners) # 这个为坐标
# 画出角点
for i in corners:
# i 相当于corners中的每一行数据
# ravel() 二维数据变为一维数据
x, y = i.ravel()
cv2.circle(img, (x, y), 3, (0, 255, 0), -1) # 实心圆
cv2.imshow("shi-tomasi", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
如果在这 16 个像素点中存在 n 个连续像素点的灰度值都高于 Ip + t,或者低于 Ip − t,那么像素点 p 就被认为是一个角点。如上图中的虚线所示,n 选取的值为 12。
为了获得更快的效果,还采用了而外的加速办法。首先对候选点的周围每个 90 度的点: 1, 9, 5, 13 进行测试(先测试 1 和 19, 如果它们符合阈值要求再测试 5 和 13)。如果 p 是角点,那么这四个点中至少有 3 个要符合阈值要求。如果不是的话肯定不是角点,就放弃。对通过这步测试的点再继续进行测试(是否有 12 的点符合阈值要求)。
这个检测器的效率很高,但是它有如下几条缺点:
• 当 n<12 时它不会丢弃很多候选点 (获得的候选点比较多)。
• 像素的选取不是最优的,因为它的效果取决与要解决的问题和角点的分布情况。
• 高速测试的结果被抛弃
• 检测到的很多特征点都是连在一起的。
前 3 个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑制的方法解决。
选择一组训练图片(最好是跟最后应用相关的图片)
使用 FAST 算法找出每幅图像的特征点
对每一个特征点,将其周围的 16 个像素存储构成一个向量。对所有图像都这样做构建一个特征向量 P
每一个特征点的 16 像素点都属于下列三类中的一种
根据这些像素点的分类,特征向量 P 也被分为 3 个子集: Pd, Ps, Pb
定义一个新的布尔变量 Kp,如果 p 是角点就设置为 Ture,如果不是就设置为 False。
使用 ID3 算法(决策树分类器)使用ID3算法(决策树分类器)使用变量Kp查询每个子集,了解真实类的知识。 它选择产生关于候选像素是否是由Kp的熵测量的角的最多信息的x
上述方法递归地应用于所有子集,直到其熵为零。
将构建好的决策树运用于其他图像的快速的检测
使用极大值抑制的方法可以解决检测到的特征点相连的问题:
import cv2.cv2 as cv2
# 实例化对象
fast = cv2.FastFeatureDetector_create(threshold=10, nonmaxSuppression=True) # 实例化一个对象
"""
:threshold: 阈值,默认为10
:nonmaxSuppression: 是否进行非极大值抑制,默认为True
:return 返回创建的对象
"""
# 进行关键点检测
kp = fast.detect(img1) # 可以用传入彩色图像,返回关键点信息
# fast.setNonmaxSuppression(False) # 取消非极大值抑制
cv2.drawKeypoints(img1, kp, img2) # 进行角点的绘制
cv2.imshow("a", img2) # 展示图片
cv2.waitKey(0) # 绘制图片
cv2.destroyAllWindows()
ORB算法是FAST算法和BRIEF算法的结合,ORB可以用来对图像中的关键点快速创建特征向量,并用这些特征向量来识别图像中的对象。即,提出了构造金字塔,为Fast特征点添加了方向,从而使得关键点具有了尺度不变性。
具体实现原理较为复杂,请到百度自行搜索
import cv2.cv2 as cv2
# 创建ORB对象
orb = cv2.ORB_create(nfeatures) # 实例化ORB对象
"""
:nfeatrues: 最大特征数量
"""
# 进行关键点检测
kp, des = orb.detectAndCompute(gray)
"""
:gray: 进行关键点检测的图像,注意是灰度图像
:return:
:kp: 关键点信息
:des: 关键点描述符,每个关键点BRIEF特征向量,二进制字符串
"""
# 将关键点检测结果绘制在图像上
cv2.drawKeypoints(img1, kp, img2)
cv2.imshow("a", img2) # 展示图片
cv2.waitKey(0) # 绘制图片
cv2.destroyAllWindows()