在图像处理领域,OpenCV(Open Source Computer Vision Library)是一个广泛使用的计算机视觉库。它提供了丰富的函数接口,帮助我们处理图像和视频数据。本文将通过实例深入探讨OpenCV中常见的操作,帮助大家理解每个操作的核心原理,并通过代码示例加以实现。
目录
一、opencv常见操作
1.格式转换操作
1.1 灰度图转换
- 通俗理解:将彩色图像转换为灰度图,常用于简化图像处理的复杂性,通常用于图像的预处理阶段。
- 核心原理:从 BGR 颜色空间转换为灰度图,灰度图只保留亮度信息,去除色彩信息。
- 代码示例:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - 参数说明:
img: 输入图像,通常为彩色图像。cv2.COLOR_BGR2GRAY: 指定颜色空间转换类型,将 BGR 图像转换为灰度图。
1.2 HSV转换
- 通俗理解:将彩色图像转换为 HSV 颜色空间,HSV 更符合人眼感知的颜色模式,常用于颜色分割和特定颜色区域的检测。
- 核心原理:通过色调、饱和度和亮度的三个分量来表示颜色,便于对颜色进行处理和分析。
- 代码示例:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - 参数说明:
img: 输入图像,通常为彩色图像。cv2.COLOR_BGR2HSV: 指定颜色空间转换类型,将 BGR 图像转换为 HSV 图像。
2. 二值化操作
- 通俗理解:将图像转换为黑白图像,通过设定一个阈值来决定哪些区域是白色,哪些是黑色,常用于图像的分割。
- 核心原理:通过设定一个阈值,低于阈值的像素值变为黑色(0),高于阈值的像素值变为白色(255)。
- 代码示例:
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) - 参数说明:
img_gray: 输入的灰度图像。127: 阈值,低于该值的像素设为 0(黑),高于该值的像素设为 255(白)。255: 最大值,即像素值超过阈值时赋值为 255。cv2.THRESH_BINARY_INV: 反向二值化,像素大于阈值设为 0,小于阈值设为 255。
3. 滤波操作
3.1 均值滤波
- 通俗理解:用一个小窗口对图像进行模糊处理,降低图像中的噪声,产生柔和效果。
- 核心原理:使用一个滑动窗口(如 3x3),对每个像素及其邻域像素进行平均,来平滑图像。
- 代码示例:
blur = cv2.blur(img, (3, 3)) - 参数说明:
img: 输入图像。(3, 3): 卷积核的大小,决定了模糊效果的程度。较大的卷积核会产生更强的模糊效果。
3.2 方框滤波
- 通俗理解:类似于均值滤波,但可以通过设置
normalize来控制是否归一化,常用于图像去噪。 - 核心原理:通过滑动一个方形窗口,对每个窗口内的像素值求和并赋值给中心像素。可以控制是否归一化窗口内的像素总和。
- 代码示例:
box = cv2.boxFilter(img, -1, (3, 3), normalize=True) - 参数说明:
img: 输入图像。-1: 输出图像深度与输入图像相同。(3, 3): 卷积核大小,决定了处理区域的大小。normalize=True: 是否归一化,True会使得窗口内像素的总和为 1,False则不归一化。
3.3 高斯滤波
- 通俗理解:通过高斯函数进行模糊,能够更加平滑地去除噪声,常用于去噪和平滑图像。
- 核心原理:使用一个高斯核(加权的卷积核)进行图像模糊,较中心的像素会有较大的权重,离中心较远的像素权重较小。
- 代码示例:
gaussian = cv2.GaussianBlur(img, (5, 5), 1) - 参数说明:
img: 输入图像。(5, 5): 高斯核的大小,通常为奇数。较大的核会产生更强的模糊效果。1: 高斯函数的标准差,较大的标准差会产生更强的模糊效果。
3.4 中值滤波
- 通俗理解:主要用于去除椒盐噪声,通过替换每个像素为邻域像素的中值来去噪。
- 核心原理:对图像中的每个像素,取其邻域像素的中位数值来替换该像素值,有效去除椒盐噪声。
- 代码示例:
median = cv2.medianBlur(img, 5) - 参数说明:
img: 输入图像。5: 卷积核的大小,通常是奇数,决定了去噪的范围。
4.形态学操作
形态学操作(闭运算、梯度运算、礼帽和黑帽)都是基于形态学变换的,它们可以帮助在图像处理过程中提取和增强图像的细节,通常用于噪声去除、物体边界提取、图像分割等任务。
4.1 腐蚀操作
- 通俗理解:腐蚀操作使得图像中的亮区域变小,暗区域变大,常用于去除小的噪点。
- 核心原理:通过一个卷积核,将图像的每个像素与卷积核进行运算,只有当卷积核的全部区域都为“亮”时,中心像素才保持不变,其他情况下会变为“暗”。
- 代码示例:
erosion = cv2.erode(img, kernel, iterations=1) - 参数说明:
img: 输入图像。kernel: 卷积核,控制腐蚀的形态。iterations=1: 腐蚀的迭代次数,次数越多,腐蚀效果越明显。
4.2 膨胀操作
- 通俗理解:膨胀操作会扩展图像中的亮区域,常用于填补腐蚀操作后留下的空洞。
- 核心原理:通过卷积核,将图像的亮区域扩展,暗区域变小,能填补图像中的小孔洞。
- 代码示例:
dilate = cv2.dilate(img, kernel, iterations=1) - 参数说明:
img: 输入图像。kernel: 卷积核,控制膨胀的形态。iterations=1: 膨胀的迭代次数,次数越多,膨胀效果越明显。
4.3 开运算(先腐蚀再膨胀)
- 通俗理解:开运算是先腐蚀后膨胀,通常用于去除图像中的小亮点或噪声。
- 核心原理:开运算先腐蚀图像,去除小的亮点,再膨胀图像,恢复形状。
- 代码示例:
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) - 参数说明:
img: 输入图像。kernel: 卷积核,控制操作的形态。cv2.MORPH_OPEN: 选择开运算。如果换成cv2.MORPH_CLOSE,则会执行闭运算(先膨胀后腐蚀)。
抱歉,遗漏了这部分内容。接下来我将按照你要求的格式,补充关于闭运算、梯度运算、礼帽和黑帽操作的详细介绍。
4.4 闭运算(先膨胀再腐蚀)
- 通俗理解:闭运算通常用于填补图像中的小孔洞,它通过膨胀和腐蚀操作来去除噪声和空白区域。
- 核心原理:闭运算是先膨胀图像,再进行腐蚀。膨胀操作先扩展亮区域,填补小孔洞,然后腐蚀操作恢复图像的结构,去除不需要的噪声。
- 代码示例:
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) - 参数说明:
img: 输入图像。kernel: 卷积核,控制操作的形态。cv2.MORPH_CLOSE: 选择闭运算。如果换成cv2.MORPH_OPEN,则会执行开运算(先腐蚀后膨胀)。
4.5 梯度运算
- 通俗理解:梯度运算用于提取图像中的边缘,通过计算膨胀和腐蚀的差异来突出显示图像的边界。
- 核心原理:梯度运算计算图像膨胀和腐蚀结果的差异,强调图像的边缘信息。它可以突出显示物体轮廓。
- 代码示例:
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) - 参数说明:
img: 输入图像。kernel: 卷积核,控制操作的形态。cv2.MORPH_GRADIENT: 选择梯度运算,计算膨胀和腐蚀的差异。
4.6 礼帽(Top Hat)
- 通俗理解:礼帽操作用于提取图像中的小亮点。它是通过图像和开运算结果的差异来提取亮点。
- 核心原理:礼帽操作计算图像与开运算结果的差异,能够突出显示图像中的小亮点或细节,常用于图像中的小目标检测。
- 代码示例:
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel) - 参数说明:
img: 输入图像。kernel: 卷积核,控制操作的形态。cv2.MORPH_TOPHAT: 选择礼帽操作,计算图像和开运算结果的差异。
4.7 黑帽(Black Hat)
- 通俗理解:黑帽操作用于提取图像中的暗点。它是通过图像和闭运算结果的差异来提取暗点。
- 核心原理:黑帽操作计算图像与闭运算结果的差异,能够突出显示图像中的暗点或细节,常用于背景分离和噪声去除。
- 代码示例:
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel) - 参数说明:
img: 输入图像。kernel: 卷积核,控制操作的形态。cv2.MORPH_BLACKHAT: 选择黑帽操作,计算图像和闭运算结果的差异。
5.边缘检测
5.1 Sobel算子
- 通俗理解:Sobel算子用于计算图像的梯度,可以帮助检测图像中的边缘信息。它通过对图像进行卷积操作,计算每个像素在水平方向(X轴)或垂直方向(Y轴)的变化。
- 核心原理:Sobel算子通过分别计算X方向和Y方向的梯度(边缘的强度变化),然后结合这两个方向的信息来检测图像的边缘。常用的卷积核大小为3或5。
- 代码示例:
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) sobelx = cv2.convertScaleAbs(sobelx) # 转换为8位无符号整数图像 sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) sobely = cv2.convertScaleAbs(sobely) sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0) # 合并X和Y方向的梯度 - 参数说明:
img: 输入图像。cv2.CV_64F: 输出图像的数据类型,64位浮点数。1, 0或0, 1: 分别表示在X轴或Y轴方向计算梯度。ksize=3: 卷积核的大小,通常为3或5。
5.2 Scharr算子
- 通俗理解:Scharr算子是比Sobel算子更精细的边缘检测算子,具有更高的精度。
- 核心原理:Scharr算子与Sobel算子类似,但它通过更大的权重调整计算,以提高图像梯度计算的精度。它适用于需要更高精度边缘检测的情况。
- 代码示例:
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0) scharrx = cv2.convertScaleAbs(scharrx) scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1) scharry = cv2.convertScaleAbs(scharry) scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0) - 参数说明:
img: 输入图像。cv2.CV_64F: 输出图像的数据类型,64位浮点数。1, 0或0, 1: 分别表示在X轴或Y轴方向计算梯度。
5.3 Laplacian算子
- 通俗理解:Laplacian算子用于计算图像的二阶导数,通常用于检测图像的边缘,特别是对噪声敏感的情况。
- 核心原理:拉普拉斯算子通过计算图像的二阶导数来检测边缘,能够突出显示图像中的强边缘和细节区域。它对图像的亮度变化比较敏感,可以有效检测图像中的边缘。
- 代码示例:
laplacian = cv2.Laplacian(img, cv2.CV_64F) laplacian = cv2.convertScaleAbs(laplacian) - 参数说明:
img: 输入图像。cv2.CV_64F: 输出图像的数据类型,64位浮点数。
5.4 Canny边缘检测
- 通俗理解:Canny边缘检测是一种多阶段的边缘检测算法,通过图像的梯度信息来检测边缘,能够精确地找出图像中的边缘。
- 核心原理:Canny算法首先进行高斯平滑,减少噪声,然后计算梯度强度,并使用双阈值来确定边缘。它采用了边缘连接技术,能够提取清晰且连贯的边缘。
- 代码示例:
v1 = cv2.Canny(img, 80, 150) - 参数说明:
img: 输入图像。80和150: 边缘检测的低阈值和高阈值。低阈值用于确定强边缘,高阈值用于连接弱边缘。
6.图像金字塔
6.1 高斯金字塔
- 通俗理解:高斯金字塔用于图像的多尺度处理,通过不同的采样级别(上采样或下采样)来处理图像。
- 核心原理:高斯金字塔是通过对图像进行逐层上采样或下采样,构建出图像的不同尺度,通常用于图像金字塔、特征提取等任务。
- 代码示例:
up = cv2.pyrUp(img) # 上采样 down = cv2.pyrDown(img) # 下采样 - 参数说明:
img: 输入图像。cv2.pyrUp(): 上采样,放大图像。cv2.pyrDown(): 下采样,缩小图像。
6.2 拉普拉斯金字塔
- 通俗理解:拉普拉斯金字塔用于提取图像的多尺度细节信息,特别用于图像压缩、去噪等应用。
- 核心原理:通过计算连续两层高斯金字塔图像之间的差异,得到每一层图像的细节部分。这种方法能够有效编码图像的多尺度信息。
- 代码示例:
down = cv2.pyrDown(img) down_up = cv2.pyrUp(down) l_1 = img - down_up # 图像与上采样后的结果相减,得到细节 - 参数说明:
img: 输入图像。cv2.pyrDown(): 下采样。cv2.pyrUp(): 上采样。
7.图像轮廓
7.1 查找轮廓
- 通俗理解:查找图像中的轮廓是一种常用的边缘检测方法,它帮助提取图像中的形状和物体的边界。
- 核心原理:通过图像的二值化或边缘检测结果,可以找到图像中所有的轮廓,并在图像中标记出来。
- 代码示例:
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - 参数说明:
img: 输入图像。cv2.RETR_EXTERNAL: 检索模式,仅查找外部轮廓。cv2.CHAIN_APPROX_SIMPLE: 轮廓逼近方法,简化轮廓信息。
7.2 绘制轮廓
- 通俗理解:绘制轮廓将提取出的轮廓显示到图像上,可以帮助分析图像的形状和结构。
- 核心原理:通过将轮廓绘制到图像上,可以直观地查看物体的边界。
- 代码示例:
draw_img = img.copy() res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) - 参数说明:
draw_img: 复制图像以便绘制。contours: 轮廓数据。-1: 绘制所有轮廓。(0, 0, 255): 轮廓的颜色(红色)。2: 轮廓线宽度。
7.3 轮廓特征
- 通俗理解:计算轮廓的特征(如面积、周长)有助于分析图像中的物体形状。
- 核心原理:轮廓的面积和周长是描述物体形状的重要特征,可以帮助进行目标检测和形状分析。
- 代码示例:
area = cv2.contourArea(cnt) # 计算面积 perimeter = cv2.arcLength(cnt, True) # 计算周长 - 参数说明:
cnt: 输入轮廓。cv2.contourArea(): 计算轮廓的面积。cv2.arcLength:计算轮廓的周长,True`表示闭合轮廓。
7.4 轮廓近似
- 通俗理解:轮廓近似用于简化轮廓,减少轮廓的点数,使得轮廓更加简洁。
- 核心原理:通过近似轮廓,减少不必要的点,保留图像的基本形状。
- 代码示例:
epsilon = 0.15 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) - 参数说明:
epsilon: 近似的精度,表示从原始轮廓到近似轮廓的最大距离。cnt: 输入的轮廓。
7.5 外接矩形
- 通俗理解:外接矩形用于获取图像中物体的边界框,常用于目标检测和物体定位。
- 核心原理:通过计算轮廓的外接矩形,获得物体的最小边界框。
- 代码示例:
x, y, w, h = cv2.boundingRect(cnt) img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) - 参数说明:
cnt: 输入的轮廓。cv2.boundingRect(): 获取轮廓的外接矩形。
7.6 外接圆
- 通俗理解:外接圆用于获取图像中物体的最小圆形边界,常用于圆形物体检测。
- 核心原理:通过计算轮廓的外接圆,得到物体的最小圆形边界。
- 代码示例:
(x, y), radius = cv2.minEnclosingCircle(cnt) center = (int(x), int(y)) radius = int(radius) img = cv2.circle(img, center, radius, (0, 255, 0), 2) - 参数说明:
cnt: 输入的轮廓。cv2.minEnclosingCircle(): 获取轮廓的外接圆。
二、OpenCV 项目实战:文档扫描
在这篇文章中,我们将通过一个完整的项目实战来巩固我们学习的 OpenCV 基本操作。本项目的目标是从一张扫描图像中提取出清晰的文档,并进行透视变换,最终输出经过处理的扫描图像。
1. 项目整体流程
首先,我们简要概括一下整个流程:
- 导入工具包
- 设置解析器对象
- 读取输入图像
- 图像预处理:灰度化、模糊和边缘检测
- 轮廓检测
- 遍历轮廓,查找矩形(4个点)
- 透视变换
- 二值化处理和保存结果
接下来,我们将逐步解析这些操作,并介绍其中涉及到的 OpenCV 常见操作。
2. 完整代码
import numpy as np
import argparse
import cv2
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image to be scanned")
args = vars(ap.parse_args())
def order_points(pts):
# 一共4个坐标点
rect = np.zeros((4, 2), dtype="float32")
# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# 计算右上和左下
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 计算输入的 w 和 h 值
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter)
return resized
# 读取输入
image = cv2.imread(args["image"])
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height=500)
# 预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 展示预处理结果
print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
# 遍历轮廓
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# 找到 4 个点的轮廓
if len(approx) == 4:
screenCnt = approx
break
# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 二值处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)
# 展示最终结果
print("STEP 3: 变换")
cv2.imshow("Original", resize(orig, height=650))
cv2.imshow("Scanned", resize(ref, height=650))
cv2.waitKey(0)
3. 步骤解析
3.1 读取输入图像并设置参数
在项目的开始部分,我们通过命令行解析器读取输入的图像路径,并且通过 resize() 函数调整图像的大小,以便更好地进行后续处理。
3.2 预处理:灰度化、模糊和边缘检测
我们通过 cv2.cvtColor() 将图像从 BGR 转为灰度图,接着使用 cv2.GaussianBlur() 对图像进行模糊处理,然后利用 cv2.Canny() 进行边缘检测。边缘检测的核心是通过梯度计算寻找图像中的显著变化点,也就是边缘。
在这部分,主要使用了以下 OpenCV 操作:
cv2.cvtColor():颜色空间转换,转换为灰度图。cv2.GaussianBlur():高斯模糊,用于去噪。cv2.Canny():Canny 边缘检测,用于提取图像边缘。


3.3 轮廓检测
在边缘图像的基础上,我们使用 cv2.findContours() 来查找图像中的轮廓,使用 cv2.contourArea() 对轮廓进行排序,取面积最大的前五个轮廓。接着,我们使用 cv2.approxPolyDP() 对每个轮廓进行多边形逼近,最终找出包含四个点的矩形轮廓。
涉及的 OpenCV 操作:
cv2.findContours():查找轮廓。cv2.contourArea():计算轮廓的面积。cv2.approxPolyDP():轮廓多边形逼近。

3.4 透视变换
当我们找到文档的四个边缘点后,我们通过 cv2.getPerspectiveTransform() 计算透视变换矩阵,并使用 cv2.warpPerspective() 将原图进行透视变换,得到一个矩形化的图像。
涉及的 OpenCV 操作:
cv2.getPerspectiveTransform():获取透视变换矩阵。cv2.warpPerspective():执行透视变换。

3.5 二值化处理
为了进一步增强图像的对比度,我们使用 cv2.threshold() 对透视变换后的图像进行二值化处理,将图像转为黑白图像。
涉及的 OpenCV 操作:
cv2.threshold():图像二值化。

3.6 保存结果
最终,我们将二值化后的图像保存为文件,并通过 cv2.imshow() 展示原图与扫描图像的对比效果。
9万+

被折叠的 条评论
为什么被折叠?



