Python版本opencv1
OpenCV 中的图像边框填充(cv.copyMakeBorder()) cv.copyMakeBorder() 是 OpenCV 中处理图像边界的关键函数,在图像处理中扮演着重要角色,特别是在以下场景:
卷积操作:在进行滤波、边缘检测等操作时,需要处理图像边界 特征提取:某些特征描述符需要处理边界像素 图像增强:为图像添加相框效果 几何变换:在旋转、缩放等操作前扩展图像
dst = cv.copyMakeBorder(
src, # 源图像
top, # 顶部填充像素数
bottom, # 底部填充像素数
left, # 左侧填充像素数
right, # 右侧填充像素数
borderType, # 边框类型
value=None # 边框颜色(仅用于BORDER_CONSTANT)
)
cv.BORDER_CONSTANT - 恒定颜色边框
# 添加灰色边框(value=[128,128,128])
[..........]
[..abcdefgh..]
[..........]
cv.BORDER_REFLECT - 镜像边框
fedcba|abcdefgh|hgfedcb
^^^^^ ^^^^^^^
左边框 右边框
cv.BORDER_REFLECT_101 - 镜像边框(最常用)
gfedcb|abcdefgh|gfedcba
^^^^^ ^^^^^^^
左边框 右边框
cv.BORDER_REPLICATE - 复制边框
aaaaaa|abcdefgh|hhhhhhh
^^^^^ ^^^^^^^
左边框 右边框
cv.BORDER_WRAP - 循环边框
# 循环边框
cdefgh|abcdefgh|abcdefg
^^^^^ ^^^^^^^
左边框 右边框
图像上的算术运算
图像加法 您可以通过OpenCV函数cv.add()或仅通过numpy操作res = img1 + img2添加两个图像。两个图像应具有相同的深度和类型,或者第二个图像可以只是一个标量值。
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]
>>> print( x+y ) # 250+10 = 260 % 256 = 4
[4]
二者作用不一样,opencv是最大就是最大,进行饱和运算;而np是会取模的
图像融合
也是图像加法,但是给了特定的权重
img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()
图像位运算
对两个图像的每个像素进行二进制操作,可以实现图像的逻辑运算、位运算等。 mask参数: 8位单通道图像(二值图像) 只在mask不为0的位置执行操作 例如在cv.bitwise_and()中:dst = src1 ∧ src2 if mask ≠ 0 这包括按位 AND、 OR、NOT 和 XOR 操作
cv.bitwise_and(src1, src2[, dst[, mask]])
cv.bitwise_or(src1, src2[, dst[, mask]])
cv.bitwise_not(src[, dst[, mask]])
cv.bitwise_xor(src1, src2[, dst[, mask]])
cv.getTickCount函数返回从参考事件(如打开机器的那一刻)到调用此函数那一刻之间的时钟周期数。因此,如果在函数执行之前和之后调用它,则会获得用于执行函数的时钟周期数。
cv.getTickFrequency函数返回时钟周期的频率或每秒的时钟周期数
OpenCV中的默认优化 许多 OpenCV 函数都是使用 SSE2、 AVX 等进行优化的。 它还包含未优化的代码。因此,如果我们的系统支持这些特性,我们就应该利用它们(几乎所有现代的处理器都支持它们)。在编译时默认启用它。因此,如果启用了 OpenCV,它将运行优化的代码,否则它将运行未优化的代码。你可以使用 cvUseoptimized 检查是否启用 / 禁用和 cvSetuseoptimized 以启用 / 禁用它。让我们看一个简单的例子。
改变颜色空间
cvtColor(input_image, flag),其中flag决定转换的类型。 对于BGR→灰度转换,我们使用标志cv.COLOR_BGR2GRAY。类似地,对于BGR→HSV,我们使用标志cv.COLOR_BGR2HSV。
图像缩放
cv2.warpAffine(src, M, dsize, dst, flags, borderMode, borderValue) → dst 仿射变换 用途:执行仿射变换(线性变换+平移) 参数要求:需要2×3变换矩阵 支持变换类型:缩放、平移、旋转、仿射变换 cv.warpPerspective() 用途:执行透视变换 参数要求:需要3×3变换矩阵 特点:直线在变换后仍保持直线
cv.resize(img, dsize=None, fx=scale_x, fy=scale_y, interpolation=)
cv.INTER_AREA:缩小图像时推荐(区域插值) cv.INTER_CUBIC:放大时使用(效果更好但较慢) cv.INTER_LINEAR:默认方法(平衡速度和质量) 平移 平移是物体位置的移动。如果您知道在(x,y)方向上的位移,则将其设为(tx,ty),则可以对图像进行平移。 M=[[1,0,tx],[0,1,ty]] 如下所示: 
对于这个函数的实现 一、平移变换的数学基础 平移变换的本质是:在二维平面上,所有点沿特定方向移动固定距离
数学表达式: x’ = x + t_x y’ = y + t_y 其中: (x, y) 是原坐标 (x’, y’) 是新坐标 t_x 是x方向位移 t_y 是y方向位移 二、齐次坐标:解决平移问题的关键 在标准二维坐标系统中,平移无法用简单的矩阵乘法表示(因为平移不是线性变换)。齐次坐标解决了这个问题: 将二维点 (x, y) 表示为三维向量: [x, y, 1]^T 平移变换矩阵扩展为3×3形式: [1 0 t_x] [0 1 t_y] [0 0 1 ] 变换计算: [x’] [1 0 t_x] [x] [y’] = [0 1 t_y] [y] [1 ] [0 0 1 ] [1] 展开: x’ = 1x + 0y + t_x1 = x + t_x y’ = 0x + 1y + t_y1 = y + t_y 1 = 0x + 0y + 1 * 1 = 1
旋转变换 M = cv.getRotationMatrix2D(center, angle, scale) 可以以不同的点作为旋转点 center:旋转中心坐标,是一个元组参数(col, row)
angle:旋转角度,旋转方向,负号为逆时针,正号为顺时针
scale:旋转后图像相比原来的缩放比例,1为等比例缩放
图像阈值
cv.threshold和cv.adaptiveThreshold cv.threshold(src, thresh, maxval, type) 第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是分配给超过阈值的像素值的最大值。OpenCV提供了不同类型的阈值,这由函数的第四个参数给出。
enum ThresholdTypes {
THRESH_BINARY = 0,#大于阈值的像素设为最大值(通常为255),其余设为0
THRESH_BINARY_INV = 1,#大于阈值的像素设为0,其余设为最大值
THRESH_TRUNC = 2,#大于阈值的像素被截断为阈值,其余保持不变
THRESH_TOZERO = 3,#小于或等于阈值的像素设为0,其余保持不变
THRESH_TOZERO_INV = 4,#大于阈值的像素设为0,其余保持不变
THRESH_MASK = 7,
THRESH_OTSU = 8,
THRESH_TRIANGLE = 16
};
自适应阈值
cv.adaptiveThreshold
cv.adaptiveThreshold(
src, # 输入图像(单通道,8位)
maxValue, # 超过阈值时分配的最大值(通常为255)
adaptiveMethod, # 自适应方法(见下文)
thresholdType, # 阈值类型(见下文)
blockSize, # 邻域大小(奇数,大于1)
C # 常量(从计算值中减去的值)
)
Otsu的二值化 在全局阈值化中,我们使用任意选择的值作为阈值。相反,Otsu的方法避免了必须选择一个值并自动确定它的情况。
考虑仅具有两个不同图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。类似地,Otsu的方法从图像直方图中确定最佳全局阈值。
thresh,ig2 = cv2.threshold(ig1,140,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)
图像平滑
图像滤波处理,做卷积操作 cv.blur()或cv.boxFilter() 操作就是卷积取平均值,区别是boxFilter是用方框,可以不用标准化,blur是用标准化的方框 高斯模糊 在这种情况下,代替盒式滤波器,使用了高斯核。这是通过功能cv.GaussianBlur() 完成的。我们应指定内核的宽度和高度,该宽度和高度应为正数和奇数。我们还应指定X和Y方向的标准偏差,分别为sigmaX和sigmaY。如果仅指定sigmaX,则将sigmaY与sigmaX相同。如果两个都为零,则根据内核大小进行计算。高斯模糊对于从图像中去除高斯噪声非常有效。
如果需要,可以使用函数cv.getGaussianKernel() 创建高斯内核。
形态学转换
cv.erode()和cv.dilate() 侵蚀 cv.erode() 侵蚀的基本思想就像土壤侵蚀一样,它侵蚀前景物体的边界(尽量使前景保持白色)。它是做什么的呢?内核滑动通过图像(在2D卷积中)。原始图像中的一个像素(无论是1还是0)只有当内核下的所有像素都是1时才被认为是1,否则它就会被侵蚀(变成0)。 需要传入kernel参数,以及对应的图像还有迭代次数 dilate跟erode在参数上是一样的,但dilate是扩张的意思,它会扩张前景物体的边界(使前景变黑色),可以看作是erode的逆操作。 开放只是侵蚀然后扩张的另一个名称。如上文所述,它对于消除噪音很有用。在这里,我们使用函数cv.morphologyEx()
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
闭运算与开运算相反,先扩张然后再侵蚀。在关闭前景对象内部的小孔或对象上的小黑点时很有用。
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
形态学梯度
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
扩展的部分 顶帽 它是输入图像和图像开运算之差。
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
黑帽 它是图像闭运算和输入图像之差。
blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
sobel算子 右减左,上减下 水平梯度矩阵G=[[-1,0,1],[-2,0,2],[-1,0,1]] 垂直梯度矩阵G=[[-1,-2,-1],[0,0,0],[1,2,1]]
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=3)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=3)
img需要为灰度图,cv.CV_64F为图像深度,dx = 1, dy = 0:计算 x 方向(水平)导数 → 检测垂直边缘 dx = 0, dy = 1:计算 y 方向(垂直)导数 → 检测水平边缘Sobel 核的大小:5×5 的核 可选值:-1 (3×3 Scharr 核)、1、3、5、7
scharr算子 x方向:[[-3,0,3],[-10,0,10],[-3,0,3]] y方向:[[-3,-10,-3],[0,0,0],[3,10,3]]
整体计算跟sobel一样,但是使用scharr核,更敏感 laplacian算子 Laplacian算子也是用来计算图像梯度的,作用也是提取边界。它与上面两种算子的不同是: sobel算子和scharr算子一般先算一个水平梯度,再算一个垂直方向梯度,然后把两个结果按照0.5的权重进行图像融合以得到完整的边界。
但Laplacian算子则不同,它本身就是一个二阶的算子,它的运算规则就是在水平方向运算两次,垂直方向运算两次,两个结果相叠加替换中心点(锚点)的像素值(灰度值)
kernel = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]],np.float32)
laplacian = cv.Laplacian(img,cv.CV_64F)
Canney边缘检测
Canny边缘检测是一种基于图像梯度的边缘检测算法。 ·1)使用高斯滤波器,以平滑图像,滤除噪声。 ·2)计算图像中每个像素点的梯度强度和方向。 ·3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。 .4)应用双闽值(Double-Threshold)检测来确定真实的和潜在的边缘。 .5)通过抑制孤立的弱边缘最终完成边缘检测。 edges = cv.Canny(img,100,200)
非极大值抑制
顾名思义,抑制不是最大值的其他值 线性插值法 取一条线经过两条边,产生两个交点,如果中间的点的梯度小于这两个交点,就会被抑制 简化计算就是一个像素点的梯度发散到他的八个方向上 +—–+—–+—–+ | ↖ | ↑ | ↗ | → 离散的8个方向 +—–+—–+—–+ | ← | ● | → | ● 中心像素(当前处理点) +—–+—–+—–+ | ↙ | ↓ | ↘ | ← 通过箭头判断比较方向 +—–+—–+—–+ Step 1️⃣:确定中心像素的梯度方向 如图中红点表示的当前像素 Step 2️⃣:找出梯度方向线上的相邻像素 例如: 若梯度方向→,则比较左右像素 若梯度方向↗,则比较右上和左下像素 Step 3️⃣:抑制非极大值 if 中心像素梯度值 < 相邻像素1的梯度值 or 中心像素梯度值 < 相邻像素2的梯度值: 将中心像素值设为0(抑制) else: 保留中心像素值(这是真实边缘)
双阈值检测
他有两个梯度阈值maxval和minval 如果梯度值大于maxval,那么则处理为边界 如果梯度值小于maxval大于minval,如果连接边界则保留,没有则舍弃 小于minval则舍弃 canny边缘检测就是使用双阈值检测
edges = cv.Canny(img,100,200)
Enjoy Reading This Article?
Here are some more articles you might like to read next:
- Google Gemini updates: Flash 1.5, Gemma 2 and Project Astra
- Displaying External Posts on Your al-folio Blog
- Graph RAG with Milvus —— 纯向量库造图的多跳推理
- Hierarchical Indices 层级索引 —— 先粗后细的两级检索
- HyDE 与 HyPE —— 假设检索技术的两个方向
- 二叉树刷题总结
- RAG 技术体系化分类——从 Pipeline 阶段到失败模式
- MemoRAG 记忆增强型 RAG 总结
- Microsoft GraphRAG 基于知识图谱的 RAG 总结
- Multimodal RAG with Captioning 图像描述型多模态 RAG 总结